2018-07-24 22:47:57 +02:00
|
|
|
import asyncio
|
|
|
|
|
import json
|
2018-07-25 11:56:37 +02:00
|
|
|
import logging
|
2018-08-22 19:50:32 +02:00
|
|
|
import os
|
2018-07-24 22:47:57 +02:00
|
|
|
import time
|
2018-09-15 14:52:24 +02:00
|
|
|
|
|
|
|
|
import traceback
|
2018-07-25 11:56:37 +02:00
|
|
|
from datetime import datetime
|
2018-07-24 22:47:57 +02:00
|
|
|
|
|
|
|
|
import discord
|
|
|
|
|
|
2018-09-01 19:12:33 +02:00
|
|
|
from pluralkit import db
|
2018-09-15 14:52:24 +02:00
|
|
|
from pluralkit.bot import channel_logger, commands, proxy, embeds
|
2018-07-24 22:47:57 +02:00
|
|
|
|
|
|
|
|
logging.basicConfig(level=logging.INFO, format="[%(asctime)s] [%(name)s] [%(levelname)s] %(message)s")
|
2018-09-15 14:52:24 +02:00
|
|
|
|
|
|
|
|
|
2018-07-25 11:56:37 +02:00
|
|
|
# logging.getLogger("pluralkit").setLevel(logging.DEBUG)
|
2018-07-24 22:47:57 +02:00
|
|
|
|
|
|
|
|
class PluralKitBot:
|
|
|
|
|
def __init__(self, token):
|
|
|
|
|
self.token = token
|
|
|
|
|
self.logger = logging.getLogger("pluralkit.bot")
|
|
|
|
|
|
|
|
|
|
self.client = discord.Client()
|
|
|
|
|
self.client.event(self.on_error)
|
|
|
|
|
self.client.event(self.on_ready)
|
|
|
|
|
self.client.event(self.on_message)
|
|
|
|
|
self.client.event(self.on_socket_raw_receive)
|
|
|
|
|
|
|
|
|
|
self.channel_logger = channel_logger.ChannelLogger(self.client)
|
2018-09-15 15:12:51 +02:00
|
|
|
|
2018-10-11 11:23:20 +02:00
|
|
|
self.proxy = proxy.Proxy(self.client, token, self.channel_logger)
|
2018-07-24 22:47:57 +02:00
|
|
|
|
|
|
|
|
async def on_error(self, evt, *args, **kwargs):
|
|
|
|
|
self.logger.exception("Error while handling event {} with arguments {}:".format(evt, args))
|
|
|
|
|
|
|
|
|
|
async def on_ready(self):
|
|
|
|
|
self.logger.info("Connected to Discord.")
|
|
|
|
|
self.logger.info("- Account: {}#{}".format(self.client.user.name, self.client.user.discriminator))
|
|
|
|
|
self.logger.info("- User ID: {}".format(self.client.user.id))
|
|
|
|
|
self.logger.info("- {} servers".format(len(self.client.servers)))
|
|
|
|
|
|
2018-10-30 20:25:06 +01:00
|
|
|
# Set playing message
|
|
|
|
|
# TODO: change this when merging rewrite-port branch, kwarg game -> activity
|
|
|
|
|
await self.client.change_presence(game=discord.Game(name="pk;help"))
|
|
|
|
|
|
2018-07-24 22:47:57 +02:00
|
|
|
async def on_message(self, message):
|
|
|
|
|
# Ignore bot messages
|
|
|
|
|
if message.author.bot:
|
|
|
|
|
return
|
2018-09-01 19:16:42 +02:00
|
|
|
|
2018-09-15 14:52:24 +02:00
|
|
|
try:
|
|
|
|
|
if await self.handle_command_dispatch(message):
|
|
|
|
|
return
|
2018-07-24 22:47:57 +02:00
|
|
|
|
2018-09-15 14:52:24 +02:00
|
|
|
if await self.handle_proxy_dispatch(message):
|
|
|
|
|
return
|
|
|
|
|
except Exception:
|
|
|
|
|
await self.log_error_in_channel(message)
|
2018-09-01 19:16:42 +02:00
|
|
|
|
2018-07-24 22:47:57 +02:00
|
|
|
async def on_socket_raw_receive(self, msg):
|
|
|
|
|
# Since on_reaction_add is buggy (only works for messages the bot's already cached, ie. no old messages)
|
|
|
|
|
# we parse socket data manually for the reaction add event
|
|
|
|
|
if isinstance(msg, str):
|
|
|
|
|
try:
|
|
|
|
|
msg_data = json.loads(msg)
|
|
|
|
|
if msg_data.get("t") == "MESSAGE_REACTION_ADD":
|
|
|
|
|
evt_data = msg_data.get("d")
|
|
|
|
|
if evt_data:
|
|
|
|
|
user_id = evt_data["user_id"]
|
|
|
|
|
message_id = evt_data["message_id"]
|
|
|
|
|
emoji = evt_data["emoji"]["name"]
|
|
|
|
|
|
|
|
|
|
async with self.pool.acquire() as conn:
|
|
|
|
|
await self.proxy.handle_reaction(conn, user_id, message_id, emoji)
|
|
|
|
|
elif msg_data.get("t") == "MESSAGE_DELETE":
|
|
|
|
|
evt_data = msg_data.get("d")
|
|
|
|
|
if evt_data:
|
|
|
|
|
message_id = evt_data["id"]
|
|
|
|
|
async with self.pool.acquire() as conn:
|
|
|
|
|
await self.proxy.handle_deletion(conn, message_id)
|
|
|
|
|
except ValueError:
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
async def handle_command_dispatch(self, message):
|
2018-09-07 17:34:38 +02:00
|
|
|
async with self.pool.acquire() as conn:
|
|
|
|
|
result = await commands.command_dispatch(self.client, message, conn)
|
|
|
|
|
return result
|
|
|
|
|
|
2018-07-24 22:47:57 +02:00
|
|
|
async def handle_proxy_dispatch(self, message):
|
|
|
|
|
# Try doing proxy parsing
|
|
|
|
|
async with self.pool.acquire() as conn:
|
|
|
|
|
return await self.proxy.try_proxy_message(conn, message)
|
|
|
|
|
|
2018-09-15 14:52:24 +02:00
|
|
|
async def log_error_in_channel(self, message):
|
|
|
|
|
channel_id = os.environ["LOG_CHANNEL"]
|
|
|
|
|
if not channel_id:
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
channel = self.client.get_channel(channel_id)
|
|
|
|
|
embed = embeds.exception_log(
|
|
|
|
|
message.content,
|
|
|
|
|
message.author.name,
|
|
|
|
|
message.author.discriminator,
|
|
|
|
|
message.server.id if message.server else None,
|
|
|
|
|
message.channel.id
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
await self.client.send_message(channel, "```python\n{}```".format(traceback.format_exc()), embed=embed)
|
|
|
|
|
|
2018-07-24 22:47:57 +02:00
|
|
|
async def run(self):
|
|
|
|
|
try:
|
|
|
|
|
self.logger.info("Connecting to database...")
|
2018-08-22 19:50:32 +02:00
|
|
|
self.pool = await db.connect(
|
|
|
|
|
os.environ["DATABASE_USER"],
|
|
|
|
|
os.environ["DATABASE_PASS"],
|
|
|
|
|
os.environ["DATABASE_NAME"],
|
|
|
|
|
os.environ["DATABASE_HOST"],
|
|
|
|
|
int(os.environ["DATABASE_PORT"])
|
|
|
|
|
)
|
2018-07-24 22:47:57 +02:00
|
|
|
|
|
|
|
|
self.logger.info("Attempting to create tables...")
|
|
|
|
|
async with self.pool.acquire() as conn:
|
|
|
|
|
await db.create_tables(conn)
|
|
|
|
|
|
|
|
|
|
self.logger.info("Connecting to Discord...")
|
|
|
|
|
await self.client.start(self.token)
|
|
|
|
|
finally:
|
|
|
|
|
self.logger.info("Logging out from Discord...")
|
|
|
|
|
await self.client.logout()
|