summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore152
-rw-r--r--IRC.py75
-rw-r--r--LICENSE20
-rw-r--r--README.md12
-rw-r--r--client.py55
5 files changed, 314 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..de2d5e0
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,152 @@
+# Byte-compiled / optimized / DLL files
+__pycache__/
+*.py[cod]
+*$py.class
+
+# C extensions
+*.so
+
+# Distribution / packaging
+.Python
+build/
+develop-eggs/
+dist/
+downloads/
+eggs/
+.eggs/
+lib/
+lib64/
+parts/
+sdist/
+var/
+wheels/
+share/python-wheels/
+*.egg-info/
+.installed.cfg
+*.egg
+MANIFEST
+
+# PyInstaller
+# Usually these files are written by a python script from a template
+# before PyInstaller builds the exe, so as to inject date/other infos into it.
+*.manifest
+*.spec
+
+# Installer logs
+pip-log.txt
+pip-delete-this-directory.txt
+
+# Unit test / coverage reports
+htmlcov/
+.tox/
+.nox/
+.coverage
+.coverage.*
+.cache
+nosetests.xml
+coverage.xml
+*.cover
+*.py,cover
+.hypothesis/
+.pytest_cache/
+cover/
+
+# Translations
+*.mo
+*.pot
+
+# Django stuff:
+*.log
+local_settings.py
+db.sqlite3
+db.sqlite3-journal
+
+# Flask stuff:
+instance/
+.webassets-cache
+
+# Scrapy stuff:
+.scrapy
+
+# Sphinx documentation
+docs/_build/
+
+# PyBuilder
+.pybuilder/
+target/
+
+# Jupyter Notebook
+.ipynb_checkpoints
+
+# IPython
+profile_default/
+ipython_config.py
+
+# pyenv
+# For a library or package, you might want to ignore these files since the code is
+# intended to run in multiple environments; otherwise, check them in:
+# .python-version
+
+# pipenv
+# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
+# However, in case of collaboration, if having platform-specific dependencies or dependencies
+# having no cross-platform support, pipenv may install dependencies that don't work, or not
+# install all needed dependencies.
+#Pipfile.lock
+
+# poetry
+# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
+# This is especially recommended for binary packages to ensure reproducibility, and is more
+# commonly ignored for libraries.
+# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
+#poetry.lock
+
+# PEP 582; used by e.g. github.com/David-OConnor/pyflow
+__pypackages__/
+
+# Celery stuff
+celerybeat-schedule
+celerybeat.pid
+
+# SageMath parsed files
+*.sage.py
+
+# Environments
+.env
+.venv
+env/
+venv/
+ENV/
+env.bak/
+venv.bak/
+
+# Spyder project settings
+.spyderproject
+.spyproject
+
+# Rope project settings
+.ropeproject
+
+# mkdocs documentation
+/site
+
+# mypy
+.mypy_cache/
+.dmypy.json
+dmypy.json
+
+# Pyre type checker
+.pyre/
+
+# pytype static type analyzer
+.pytype/
+
+# Cython debug symbols
+cython_debug/
+
+# PyCharm
+# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
+# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
+# and can be added to the global gitignore or merged into this file. For a more nuclear
+# option (not recommended) you can uncomment the following to ignore the entire idea folder.
+#.idea/
diff --git a/IRC.py b/IRC.py
new file mode 100644
index 0000000..430bbf0
--- /dev/null
+++ b/IRC.py
@@ -0,0 +1,75 @@
+
+import socket
+import sys
+import time
+class IRCBadMessage(Exception):
+ pass
+
+def parsemsg(s):
+ """Breaks a message from an IRC server into its prefix, command, and arguments.
+ """
+ prefix = ''
+ trailing = []
+ if not s:
+ raise IRCBadMessage("Empty line.")
+ if s[0] == ':':
+ prefix, s = s[1:].split(' ', 1)
+ if s.find(' :') != -1:
+ s, trailing = s.split(' :', 1)
+ args = s.split()
+ args.append(trailing)
+ else:
+ args = s.split()
+ command = args.pop(0)
+ return prefix, command, args
+
+LINEEND = '\r\n'
+
+class IRCBot:
+
+ irc = None
+
+ def __init__(self, sock):
+ # Define the socket
+ self.irc = sock
+
+ def send_privmsg(self, channel, msg):
+ # Transfer data
+ self.irc.send(bytes("PRIVMSG " + channel + " :" + msg + LINEEND, "UTF-8"))
+
+ def send_quit(self, quitmsg):
+ msg = f'QUIT :{quitmsg}' + LINEEND
+ print(msg)
+ self.irc.send(msg.encode('utf-8'))
+
+ def send_action(self, action_msg):
+ pass
+
+ def connect(self, server, port, channel, botnick, botpass, botnickpass):
+ # Connect to the server
+ print("Connecting to: " + server)
+ self.irc.connect((server, port))
+
+ # Perform user authentication
+ self.irc.send(bytes("USER " + botnick + " " + botnick +
+ " " + botnick + " :python" + LINEEND, "UTF-8"))
+ self.irc.send(bytes("NICK " + botnick + LINEEND, "UTF-8"))
+ self.irc.send(bytes("NICKSERV IDENTIFY " +
+ botnickpass + " " + LINEEND, "UTF-8"))
+ time.sleep(5)
+
+ # join the channel
+ self.irc.send(bytes("JOIN " + channel + LINEEND, "UTF-8"))
+
+ def get_response(self):
+ time.sleep(1)
+ # Get the response
+ resp = self.irc.recv(4096).decode("UTF-8")
+ msg = parsemsg(resp)
+
+ if msg[1] == 'PING':
+ print('Sending pong')
+ self.irc.send(
+ bytes('PONG ' + LINEEND, "UTF-8"))
+
+ return msg
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..d4b0539
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,20 @@
+Copyright (C) 2022 M Arnold
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject
+to the following conditions:
+
+The above copyright notice and this permission notice shall be included
+in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..589c75a
--- /dev/null
+++ b/README.md
@@ -0,0 +1,12 @@
+# RabbitEars IRC client builder
+
+RabbitEars is an irc client framework writen in python
+Warning this is experimental prealpha software. It works but APIs design
+models and other stuff are subject to change at my whim at this stage
+
+## Features, and Planned features
+* TLS suppport as first class citizen ✔
+* Clear license Terms ✔
+* Event driven
+* Conformity with modern irc specs
+* As few external dependancies as possible ❗
diff --git a/client.py b/client.py
new file mode 100644
index 0000000..112707c
--- /dev/null
+++ b/client.py
@@ -0,0 +1,55 @@
+#
+from IRC import *
+import os
+import random
+import ssl
+import socket
+import sys
+
+LINEEND = '\r\n'
+# IRC Config
+hostname = "irc.spartalinux.xyz" # Provide a valid server IP/Hostname
+port = 6697
+channel = "#botdev"
+botnick = "botley"
+botnickpass = "a.password"
+botpass = "unused"
+
+# Need to pass the IRCBot class a socket the reason it doesn't do this itself is
+# so you can set up TLS or not as you need it
+# These provide good defaults. But your milage may vary
+oursock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+context = ssl.SSLContext()
+context.check_hostname = False
+context.verify_mode = ssl.CERT_NONE
+oursock = context.wrap_socket(oursock, server_hostname=hostname)
+irc = IRCBot(oursock)
+irc.connect(hostname, port, channel, botnick, botpass, botnickpass)
+
+def generate_response(person, message):
+ print(person, message)
+ msg = message.strip(LINEEND)
+ irc.send_privmsg(channel, str(type(person)) + ' ' + str(type(message)))
+ if 'cool.person' in person and msg.lower() == "hello botley":
+ return "Greetings Master"
+ elif msg.lower() == "hello":
+ return "Greetings Human!"
+ else:
+ return None
+
+while True:
+ try:
+
+ text = irc.get_response()
+ print(text[0],text[1],text[2])
+ if text[1] == 'PRIVMSG' and text[2][0] == channel:
+ r = generate_response(text[0],text[2][1])
+ if r is not None:
+ irc.send_privmsg(channel,r)
+ except KeyboardInterrupt:
+ irc.send_quit("Ctrl-C Pressed")
+ msg = oursock.recv(4096)
+ print(msg)
+ sys.exit(0)
+
+