api stuff werks, client will idle and get events.

next comes plugin integration!
master
cynic 4 years ago
parent 9139adef70
commit 459496d198
  1. 1
      .gitignore
  2. 14
      README.md
  3. 48
      client.py
  4. 26
      grim_logger.py
  5. 18
      main.py

1
.gitignore vendored

@ -1,2 +1,3 @@
*.pyc *.pyc
__pycache__ __pycache__
.vscode

@ -1,2 +1,16 @@
# modular-discord-bot-fw # modular-discord-bot-fw
framework for discord bots that uses contained files for individual feature plugins that can be modified and reloaded on the fly framework for discord bots that uses contained files for individual feature plugins that can be modified and reloaded on the fly
basic structure:
* client class has implementations for handling opcodes
* event response is up to the client (!!)
* this lets us expose events for plugins to hook into, but keeps core API interaction defined in-class
* any and all bot features should be useable via independent feature plugins
* these are dynamically found and run, so they can be hotloaded, modified, etc. without disconnecting or restarting the whole bot session
todo before it's basically finished:
* basic rate limiting
* plugin manager & hook integration
* make sure broken sockets are properly addressed with a resume or reconnect
* default plugins so the bot does _something_ out of the box
* plugin reloading, user (command) authentication, etc. will also probably be written in plugin form

@ -0,0 +1,48 @@
import websockets, asyncio, json
from random import randint
import disc_api
from grim_logger import glog, glog_level
class client():
def __init__(self, token):
self.logger = glog(level = glog_level.TRACE)
self.ws = None
self.last_sequence = None
self.session_id = None
self.token = token
self.user = None
async def shit(self):
# https://discord.com/developers/docs/topics/gateway#connecting-to-the-gateway
# you're supposed to ask an HTTP endpoint what the ws gateway is but the docs didnt tell me the domain after 1sec so HAHA NOPE
async with websockets.connect("wss://gateway.discord.gg/?encoding=json&v=9") as self.ws:# they seem to be fond of making new versions randomly and still supporting old ones?
while True: # they may or may not drop support for this soon # nevermind we're on v9 now
data = json.loads(await self.ws.recv())
handler = getattr(self, "op_"+disc_api.gateway_opcodes[data["op"]], None)
self.logger.write(data)
self.last_sequence = data["s"]
if handler:
await handler(data)
async def heartbeat(self, interval):
while True:
self.logger.write(interval/1000)
await self.ws.send(json.dumps({"op":1, "d":self.last_sequence}))
await asyncio.sleep(interval/1000)
self.logger.write("sent heartbeat")
async def op_hello(self, res):
if not(self.last_sequence and self.session_id):
interval = res["d"]["heartbeat_interval"]
asyncio.get_event_loop().create_task(self.heartbeat(interval))
await asyncio.sleep(1)
# https://discord.com/developers/docs/topics/gateway#identify-example-identify
await self.ws.send(json.dumps({"op":2, "d":{"token":self.token, "intents": 513, "properties":{"$os":"linux", "browser":"mdbf", "device":"mdbf"}}}))
else:
# https://discord.com/developers/docs/topics/gateway#identify-example-identify
await self.ws.send(json.dumps({"op":6, "d":{"token":self.token, "session_id":self.session_id, "seq":self.last_sequence}}))
async def op_invalid_session(self, res):
await asyncio.sleep(randint(1, 5)) # try again
await self.ws.send(json.dumps({"op":2, "d":{"token":self.token, "intents": 513, "properties":{"$os":"linux", "browser":"mdbf", "device":"mdbf"}}}))
async def op_dispatch(self, res):
if res["t"] == "READY":
self.session_id = res["d"]["session_id"]
self.user = res["d"]["user"]
def run(self):
asyncio.run(self.shit())

@ -0,0 +1,26 @@
from enum import Enum
from inspect import stack
from sys import stdout
from datetime import datetime
class glog_flags(Enum):
METHOD_PRINT = 0
METHOD_FILE = 1
class glog_level(Enum):
PLAIN = 0
TRACE = 1
class glog():
def __init__(self, flags = [glog_flags.METHOD_PRINT], filename = "bot.log", level = glog_level.PLAIN):
if not flags: raise ValueError("no glog_flags given!")
self.targets = []
if glog_flags.METHOD_PRINT in flags:
self.targets.append(stdout)
if glog_flags.METHOD_FILE in flags:
self.targets.append(open(filename, "a"))
self.level = level
def __del__(self):
for target in self.targets:
if not(target == stdout):
target.close()
def write(self, message):
for target in self.targets:
target.write("({2})@[{0}]: {1}\n".format(stack()[1].function if self.level == glog_level.TRACE else "glog", message, datetime.now()))

@ -1,15 +1,3 @@
import websockets, asyncio, json from client import client
import disc_api olive = client("OTIxMDg0ODA3Mjk4MDU2MjAz.YbtxEg.NxS1h63qKDeTn_4voc2Axa_QIFI")
class client(): olive.run()
def __init__(self):
pass
async def shit():
# https://discord.com/developers/docs/topics/gateway#connecting-to-the-gateway
# you're supposed to ask an HTTP endpoint what the ws gateway is but the docs didnt tell me the domain after 1sec so HAHA NOPE
async with websockets.connect("wss://gateway.discord.gg/?encoding=json&v=9") as ws: # they seem to be fond of making new versions randomly and still supporting old ones?
while True: # they may or may not drop support for this soon
data = (await ws.recv())
print(data)
break
def run():
asyncio.run(shit())
Loading…
Cancel
Save