api stuff werks, client will idle and get events.

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

3
.gitignore vendored

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

@ -1,2 +1,16 @@
# 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
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())

@ -26,4 +26,4 @@ gateway_close_event_codes = { # https://discord.com/developers/docs/topics/opcod
4012: "You sent an invalid version for the gateway.",
4013: "You sent an invalid intent for a Gateway Intent. You may have incorrectly calculated the bitwise value.",
4014: "You sent a disallowed intent for a Gateway Intent. You may have tried to specify an intent that you have not enabled or are not approved for."
}
}

@ -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
import disc_api
class client():
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())
from client import client
olive = client("OTIxMDg0ODA3Mjk4MDU2MjAz.YbtxEg.NxS1h63qKDeTn_4voc2Axa_QIFI")
olive.run()
Loading…
Cancel
Save