Skip to content

Instantly share code, notes, and snippets.

@jlu5
Created October 9, 2018 05:21
Show Gist options
  • Save jlu5/78f0504c4e38ad434e1742f9454c0ef2 to your computer and use it in GitHub Desktop.
Save jlu5/78f0504c4e38ad434e1742f9454c0ef2 to your computer and use it in GitHub Desktop.
From 68e7cdd057a5a3a0a7cf8d4acf01c7daf2a10ed1 Mon Sep 17 00:00:00 2001
From: James Lu <[email protected]>
Date: Fri, 5 Oct 2018 23:39:04 -0700
Subject: [PATCH] [WIP] discord madness, pass 1
---
classes.py | 7 ++-
protocols/discord.py | 136 +++++++++++++++++++++++++++++++++++++++++++
2 files changed, 142 insertions(+), 1 deletion(-)
create mode 100644 protocols/discord.py
diff --git a/classes.py b/classes.py
index 0ced7db..0f2a918 100644
--- a/classes.py
+++ b/classes.py
@@ -229,7 +229,10 @@ class PyLinkNetworkCore(structures.CamelCaseToSnakeCase):
self.name = netname
self.conf = conf.conf
self.sid = None
- self.serverdata = conf.conf['servers'][netname]
+ try:
+ self.serverdata = conf.conf['servers'][netname]
+ except KeyError:
+ self.serverdata = {}
self.protoname = self.__class__.__module__.split('.')[-1] # Remove leading pylinkirc.protocols.
self.proto = self.irc = self # Backwards compat
@@ -258,6 +261,8 @@ class PyLinkNetworkCore(structures.CamelCaseToSnakeCase):
self.was_successful = False
+ self.virtual_parent = None
+
self._init_vars()
def log_setup(self):
diff --git a/protocols/discord.py b/protocols/discord.py
new file mode 100644
index 0000000..4a86019
--- /dev/null
+++ b/protocols/discord.py
@@ -0,0 +1,136 @@
+import discord
+import asyncio
+import time # for testing
+
+from pylinkirc import utils, conf
+from pylinkirc.classes import *
+from pylinkirc.log import log
+
+class DiscordServer(PyLinkNetworkCoreWithUtils):
+ def __init__(self, name, parent):
+ super().__init__(name)
+ self.virtual_parent = parent
+
+ def _init_vars(self):
+ super()._init_vars()
+ self.casemapping = 'ascii' # TODO: investigate utf-8 support
+ self.cmodes = {'op': 'o', 'voice': 'v'}
+
+class PyLinkDiscordProtocol(PyLinkNetworkCoreWithUtils):
+
+ def __init__(self, *args, **kwargs):
+ super().__init__(*args, **kwargs)
+ self._hooks_queue = queue.Queue()
+ self.client = discord.Client()
+
+ events = ['on_ready']
+ for fn_name in events:
+ wrapped_fn = self.client.event(getattr(self, fn_name))
+ setattr(self, fn_name, wrapped_fn)
+
+ self._children = {}
+
+ def _process_hooks(self):
+ """Loop to process incoming hook data."""
+ while not self._aborted.is_set():
+ data = self._hooks_queue.get()
+ if data is None:
+ log.debug('(%s) Stopping queue thread due to getting None as item', self.name)
+ break
+ elif self not in world.networkobjects.values():
+ log.debug('(%s) Stopping stale queue thread; no longer matches world.networkobjects', self.name)
+ break
+
+ subserver, data = data
+ if subserver not in world.networkobjects:
+ log.error('(%s) Not queuing hook for subserver %r no longer in networks list.',
+ self.name, subserver)
+ elif subserver in self._children:
+ self._children[subserver].call_hooks(data)
+
+ def _add_hook(self, subserver, data):
+ """
+ Pushes a hook payload for the given subserver.
+ """
+ if subserver not in self._children:
+ raise ValueError("Unknown subserver %s" % subserver)
+ self._hooks_queue.put_nowait((
+ subserver,
+ [None, 'ENDBURST', {}]
+ ))
+
+ def _create_child(self, name):
+ """
+ Creates a virtual network object for a server with the given name.
+ """
+ if name in world.networkobjects:
+ raise ValueError("Attempting to reintroduce network with name %r" % name)
+ child = DiscordServer(name, self)
+ world.networkobjects[name] = self._children[name] = child
+ return child
+
+ def _remove_child(self, name):
+ """
+ Removes a virtual network object with the given name.
+ """
+ self._add_hook(name, [None, 'PYLINK_DISCONNECT', {}])
+ del self._children[name]
+ del world.networkobjects[name]
+
+ def connect(self):
+ self._aborted.clear()
+ if 'token' not in self.serverdata:
+ raise ProtocolError("No API token defined under server settings")
+
+ self._queue_thread = threading.Thread(name="Queue thread for %s" % self.name,
+ target=self._process_hooks, daemon=True)
+ self._queue_thread.start()
+
+ self.client.loop.run_until_complete(
+ self.client.start(self.serverdata['token'])
+ )
+
+ def disconnect(self):
+ self._aborted.set()
+
+ log.debug('(%s) Killing hooks handler', self.name)
+ try:
+ # XXX: queue.Queue.queue isn't actually documented, so this is probably not reliable in the long run.
+ with self._hooks_queue.mutex:
+ self._hooks_queue.queue[0] = None
+ except IndexError:
+ self._hooks_queue.put(None)
+
+ log.debug('(%s) Sending Discord logout', self.name)
+
+ async def exception_to_shutdown(loop, context):
+ await self.client.logout()
+
+ # Force a shutdown by overriding the exception handler
+ #self.client.loop.set_exception_handler(exception_to_shutdown)
+ #self.client.loop.call_exception_handler(
+ # {'message': 'disconnect() called from outside event loop'}
+ #)
+ #while self.client.loop.is_running():
+ # time.sleep(0.5)
+ #self.client.loop.stop()
+ self.client.loop.run_until_complete(self.client.logout())
+
+ '''
+ async def on_message(self, message):
+ log.debug('got message %s', message)
+ await self.client.send_message(message.channel, 'Hello World!')
+ '''
+
+ @asyncio.coroutine
+ async def on_ready(self):
+ for server in self.client.servers:
+ pylink_netobj = self._create_child(server.name)
+ for channel in server.channels:
+ if channel.type == discord.ChannelType.text:
+ pylink_netobj.channels[channel.name] = Channel(pylink_netobj, name=channel.name)
+ await self.client.send_message(channel, 'Hello World! It is currently %s' % time.ctime())
+
+ self._add_hook(server.name, [None, 'ENDBURST', {}])
+
+Class = PyLinkDiscordProtocol
--
2.19.0
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment