summary refs log tree commit diff
diff options
context:
space:
mode:
authorMatt Arnold <matt@thegnuguru.org>2022-02-12 21:25:13 -0500
committerMatt Arnold <matt@thegnuguru.org>2022-02-12 21:25:13 -0500
commit7d0300a7a428dd42d7751e0e636a42c55f2e116e (patch)
tree5ec5fbcd8f3d94808dbb603f85a60743d526b0f6
inital commit, laugh all you want
-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)
+
+