1
Fork 0
mirror of https://github.com/RYGhub/royalnet.git synced 2024-11-23 19:44:20 +00:00

Working discord bot implementation! :O

This commit is contained in:
Steffo 2019-04-12 00:55:35 +02:00
parent c907422156
commit 49382b9bc1
14 changed files with 197 additions and 52 deletions

View file

@ -18,6 +18,7 @@ tg_bot = TelegramBot(os.environ["TG_AK"], "localhost:1234", "sas", commands, os.
ds_bot = DiscordBot(os.environ["DS_AK"], "localhost:1234", "sas", commands, os.environ["DB_PATH"], Royal, Discord, "discord_id", error_command=ErrorHandlerCommand) ds_bot = DiscordBot(os.environ["DS_AK"], "localhost:1234", "sas", commands, os.environ["DB_PATH"], Royal, Discord, "discord_id", error_command=ErrorHandlerCommand)
loop.create_task(master.run()) loop.create_task(master.run())
loop.create_task(tg_bot.run()) loop.create_task(tg_bot.run())
loop.create_task(ds_bot.run())
print("Commands enabled:") print("Commands enabled:")
print(tg_bot.generate_botfather_command_string()) print(tg_bot.generate_botfather_command_string())
print("Starting loop...") print("Starting loop...")

View file

@ -2,6 +2,7 @@ import discord
import asyncio import asyncio
import typing import typing
import logging as _logging import logging as _logging
import sys
from ..commands import NullCommand from ..commands import NullCommand
from ..utils import asyncify, Call, Command, UnregisteredError from ..utils import asyncify, Call, Command, UnregisteredError
from ..network import RoyalnetLink, Message from ..network import RoyalnetLink, Message
@ -16,10 +17,6 @@ async def todo(message: Message):
class DiscordBot: class DiscordBot:
# noinspection PyMethodParameters
class DiscordClient(discord.Client):
pass
def __init__(self, def __init__(self,
token: str, token: str,
master_server_uri: str, master_server_uri: str,
@ -32,13 +29,14 @@ class DiscordBot:
missing_command: typing.Type[Command] = NullCommand, missing_command: typing.Type[Command] = NullCommand,
error_command: typing.Type[Command] = NullCommand): error_command: typing.Type[Command] = NullCommand):
self.token = token self.token = token
self.bot = self.DiscordClient() self.missing_command = missing_command
self.error_command = error_command
self.network: RoyalnetLink = RoyalnetLink(master_server_uri, master_server_secret, "discord", todo) self.network: RoyalnetLink = RoyalnetLink(master_server_uri, master_server_secret, "discord", todo)
# Generate commands # Generate commands
self.commands = {} self.commands = {}
required_tables = set() required_tables = set()
for command in commands: for command in commands:
self.commands[f"/{command.command_name}"] = command self.commands[f"!{command.command_name}"] = command
required_tables = required_tables.union(command.require_alchemy_tables) required_tables = required_tables.union(command.require_alchemy_tables)
# Generate the Alchemy database # Generate the Alchemy database
self.alchemy = Alchemy(database_uri, required_tables) self.alchemy = Alchemy(database_uri, required_tables)
@ -51,6 +49,7 @@ class DiscordBot:
class DiscordCall(Call): class DiscordCall(Call):
interface_name = "discord" interface_name = "discord"
interface_obj = self interface_obj = self
interface_prefix = "!"
alchemy = self.alchemy alchemy = self.alchemy
async def reply(call, text: str): async def reply(call, text: str):
@ -85,6 +84,37 @@ class DiscordBot:
self.DiscordCall = DiscordCall self.DiscordCall = DiscordCall
# noinspection PyMethodParameters
class DiscordClient(discord.Client):
async def on_message(cli, message: discord.Message):
text = message.content
# Skip non-text messages
if not text:
return
# Find and clean parameters
command_text, *parameters = text.split(" ")
# Find the function
try:
selected_command = self.commands[command_text]
except KeyError:
# Skip inexistent commands
selected_command = self.missing_command
# Call the command
try:
return await self.DiscordCall(message.channel, selected_command, parameters, log,
message=message).run()
except Exception as exc:
try:
return await self.DiscordCall(message.channel, self.error_command, parameters, log,
message=message,
exception_info=sys.exc_info(),
previous_command=selected_command).run()
except Exception as exc2:
log.error(f"Exception in error handler command: {exc2}")
self.DiscordClient = DiscordClient
self.bot = self.DiscordClient()
async def run(self): async def run(self):
await self.bot.login(self.token) await self.bot.login(self.token)
await self.bot.connect() await self.bot.connect()

View file

@ -51,6 +51,7 @@ class TelegramBot:
class TelegramCall(Call): class TelegramCall(Call):
interface_name = "telegram" interface_name = "telegram"
interface_obj = self interface_obj = self
interface_prefix = "/"
alchemy = self.alchemy alchemy = self.alchemy
async def reply(call, text: str): async def reply(call, text: str):

View file

@ -11,10 +11,6 @@ class ErrorHandlerCommand(Command):
@classmethod @classmethod
async def common(cls, call: Call): async def common(cls, call: Call):
raise UnsupportedError()
@classmethod
async def telegram(cls, call: Call):
try: try:
e_type, e_value, e_tb = call.kwargs["exception_info"] e_type, e_value, e_tb = call.kwargs["exception_info"]
except InvalidInputError: except InvalidInputError:
@ -22,7 +18,7 @@ class ErrorHandlerCommand(Command):
return return
if e_type == InvalidInputError: if e_type == InvalidInputError:
command = call.kwargs["previous_command"] command = call.kwargs["previous_command"]
await call.reply(f"⚠️ Sintassi non valida.\nSintassi corretta: [c]/{command.command_name} {command.command_syntax}[/c]") await call.reply(f"⚠️ Sintassi non valida.\nSintassi corretta: [c]{call.interface_prefix}{command.command_name} {command.command_syntax}[/c]")
return return
if e_type == UnregisteredError: if e_type == UnregisteredError:
await call.reply("⚠️ Devi essere registrato a Royalnet per usare questo comando!") await call.reply("⚠️ Devi essere registrato a Royalnet per usare questo comando!")

View file

@ -26,7 +26,7 @@ class Alchemy:
except AttributeError: except AttributeError:
# Actually the intended result # Actually the intended result
# TODO: here is the problem! # TODO: here is the problem!
self.__setattr__(name, type(name, (self.Base,), cdj(table))) self.__setattr__(name, type(name, (self.Base, table), {}))
else: else:
raise NameError(f"{name} is a reserved name and can't be used as a table name") raise NameError(f"{name} is a reserved name and can't be used as a table name")
self.Base.metadata.create_all() self.Base.metadata.create_all()

View file

@ -3,16 +3,29 @@ from sqlalchemy import Column, \
Integer, \ Integer, \
ForeignKey ForeignKey
from sqlalchemy.orm import relationship from sqlalchemy.orm import relationship
from sqlalchemy.ext.declarative import declared_attr
from .royals import Royal
from .keygroup import Keygroup
class ActiveKvGroup: class ActiveKvGroup:
__tablename__ = "activekvgroups" __tablename__ = "activekvgroups"
royal_id = Column(Integer, ForeignKey("royals.uid"), primary_key=True) @declared_attr
group_name = Column(String, ForeignKey("keygroups.group_name"), nullable=False) def royal_id(self):
return Column(Integer, ForeignKey("royals.uid"), primary_key=True)
royal = relationship("Royal", backref="active_kv_group") @declared_attr
group = relationship("Keygroup", backref="users_with_this_active") def group_name(self):
return Column(String, ForeignKey("keygroups.group_name"), nullable=False)
@declared_attr
def royal(self):
return relationship("Royal", backref="active_kv_group")
@declared_attr
def group(self):
return relationship("Keygroup", backref="users_with_this_active")
def __repr__(self): def __repr__(self):
return f"<ActiveKvGroup royal={self.royal} group={self.group_name}>" return f"<ActiveKvGroup royal={self.royal} group={self.group_name}>"

View file

@ -3,16 +3,24 @@ from sqlalchemy import Column, \
String, \ String, \
ForeignKey ForeignKey
from sqlalchemy.orm import relationship from sqlalchemy.orm import relationship
from sqlalchemy.ext.declarative import declared_attr
from .royals import Royal from .royals import Royal
class Alias: class Alias:
__tablename__ = "aliases" __tablename__ = "aliases"
royal_id = Column(Integer, ForeignKey("royals.uid")) @declared_attr
alias = Column(String, primary_key=True) def royal_id(self):
return Column(Integer, ForeignKey("royals.uid"))
royal = relationship("Royal", backref="aliases") @declared_attr
def alias(self):
return Column(String, primary_key=True)
@declared_attr
def royal(self):
return relationship("Royal", backref="aliases")
def __repr__(self): def __repr__(self):
return f"<Alias {str(self)}>" return f"<Alias {str(self)}>"

View file

@ -7,24 +7,56 @@ from sqlalchemy import Column, \
ForeignKey, \ ForeignKey, \
String String
from sqlalchemy.orm import relationship from sqlalchemy.orm import relationship
from sqlalchemy.ext.declarative import declared_attr
from .royals import Royal from .royals import Royal
class Diario: class Diario:
__tablename__ = "diario" __tablename__ = "diario"
diario_id = Column(Integer, primary_key=True) @declared_attr
creator_id = Column(Integer, ForeignKey("royals.uid"), nullable=False) def diario_id(self):
quoted_account_id = Column(Integer, ForeignKey("royals.uid")) return Column(Integer, primary_key=True)
quoted = Column(String)
text = Column(Text)
context = Column(Text)
timestamp = Column(DateTime, nullable=False)
media_url = Column(String)
spoiler = Column(Boolean, default=False)
creator = relationship("Royal", foreign_keys=creator_id, backref="diario_created") @declared_attr
quoted_account = relationship("Royal", foreign_keys=quoted_account_id, backref="diario_quoted") def creator_id(self):
return Column(Integer, ForeignKey("royals.uid"), nullable=False)
@declared_attr
def quoted_account_id(self):
return Column(Integer, ForeignKey("royals.uid"))
@declared_attr
def quoted(self):
return Column(String)
@declared_attr
def text(self):
return Column(Text)
@declared_attr
def context(self):
return Column(Text)
@declared_attr
def timestamp(self):
return Column(DateTime, nullable=False)
@declared_attr
def media_url(self):
return Column(String)
@declared_attr
def spoiler(self):
return Column(Boolean, default=False)
@declared_attr
def creator(self):
return relationship("Royal", foreign_keys=self.creator_id, backref="diario_created")
@declared_attr
def quoted_account(self):
return relationship("Royal", foreign_keys=self.quoted_account_id, backref="diario_quoted")
def __repr__(self): def __repr__(self):
return f"<Diario diario_id={self.diario_id} creator_id={self.creator_id} quoted_account_id={self.quoted_account_id} quoted={self.quoted} text={self.text} context={self.context} timestamp={self.timestamp} media_url={self.media_url} spoiler={self.spoiler}>" return f"<Diario diario_id={self.diario_id} creator_id={self.creator_id} quoted_account_id={self.quoted_account_id} quoted={self.quoted} text={self.text} context={self.context} timestamp={self.timestamp} media_url={self.media_url} spoiler={self.spoiler}>"

View file

@ -4,19 +4,36 @@ from sqlalchemy import Column, \
BigInteger, \ BigInteger, \
ForeignKey ForeignKey
from sqlalchemy.orm import relationship from sqlalchemy.orm import relationship
from sqlalchemy.ext.declarative import declared_attr
from .royals import Royal from .royals import Royal
class Discord: class Discord:
__tablename__ = "discord" __tablename__ = "discord"
royal_id = Column(Integer, ForeignKey("royals.uid")) @declared_attr
discord_id = Column(BigInteger, primary_key=True) def royal_id(self):
username = Column(String) return Column(Integer, ForeignKey("royals.uid"))
discriminator = Column(String)
avatar_hash = Column(String)
royal = relationship("Royal", backref="discord") @declared_attr
def discord_id(self):
return Column(BigInteger, primary_key=True)
@declared_attr
def username(self):
return Column(String)
@declared_attr
def discriminator(self):
return Column(String)
@declared_attr
def avatar_hash(self):
return Column(String)
@declared_attr
def royal(self):
return relationship("Royal", backref="discord")
def __repr__(self): def __repr__(self):
return f"<Discord {str(self)}>" return f"<Discord {str(self)}>"

View file

@ -1,12 +1,15 @@
from sqlalchemy import Column, \ from sqlalchemy import Column, \
String, \ String, \
ForeignKey ForeignKey
from sqlalchemy.ext.declarative import declared_attr
class Keygroup: class Keygroup:
__tablename__ = "keygroups" __tablename__ = "keygroups"
group_name = Column(String, ForeignKey("keygroups.group_name"), primary_key=True) @declared_attr
def group_name(self):
return Column(String, ForeignKey("keygroups.group_name"), primary_key=True)
def __repr__(self): def __repr__(self):
return f"<Keygroup {self.group_name}>" return f"<Keygroup {self.group_name}>"

View file

@ -2,17 +2,28 @@ from sqlalchemy import Column, \
String, \ String, \
ForeignKey ForeignKey
from sqlalchemy.orm import relationship from sqlalchemy.orm import relationship
from sqlalchemy.ext.declarative import declared_attr
from .keygroup import Keygroup from .keygroup import Keygroup
class Keyvalue: class Keyvalue:
__tablename__ = "keyvalues" __tablename__ = "keyvalues"
group_name = Column(String, ForeignKey("keygroups.group_name"), primary_key=True) @declared_attr
key = Column(String, primary_key=True) def group_name(self):
value = Column(String, nullable=False) return Column(String, ForeignKey("keygroups.group_name"), primary_key=True)
group = relationship("Keygroup") @declared_attr
def key(self):
return Column(String, primary_key=True)
@declared_attr
def value(self):
return Column(String, nullable=False)
@declared_attr
def group(self):
return relationship("Keygroup")
def __repr__(self): def __repr__(self):
return f"<Keyvalue group={self.group_name} key={self.key} value={self.value}>" return f"<Keyvalue group={self.group_name} key={self.key} value={self.value}>"

View file

@ -2,16 +2,31 @@ from sqlalchemy import Column, \
Integer, \ Integer, \
String, \ String, \
LargeBinary LargeBinary
from sqlalchemy.ext.declarative import declared_attr
class Royal: class Royal:
__tablename__ = "royals" __tablename__ = "royals"
uid = Column(Integer, unique=True, primary_key=True) @declared_attr
username = Column(String, unique=True, nullable=False) def uid(self):
password = Column(LargeBinary) return Column(Integer, unique=True, primary_key=True)
role = Column(String, nullable=False)
avatar = Column(LargeBinary) @declared_attr
def username(self):
return Column(String, unique=True, nullable=False)
@declared_attr
def password(self):
return Column(LargeBinary)
@declared_attr
def role(self):
return Column(String, nullable=False)
@declared_attr
def avatar(self):
return Column(LargeBinary)
def __repr__(self): def __repr__(self):
return f"<Royal {self.username}>" return f"<Royal {self.username}>"

View file

@ -5,19 +5,36 @@ from sqlalchemy import Column, \
LargeBinary, \ LargeBinary, \
ForeignKey ForeignKey
from sqlalchemy.orm import relationship from sqlalchemy.orm import relationship
from sqlalchemy.ext.declarative import declared_attr
from .royals import Royal from .royals import Royal
class Telegram: class Telegram:
__tablename__ = "telegram" __tablename__ = "telegram"
royal_id = Column(Integer, ForeignKey("royals.uid")) @declared_attr
tg_id = Column(BigInteger, primary_key=True) def royal_id(self):
first_name = Column(String) return Column(Integer, ForeignKey("royals.uid"))
last_name = Column(String)
username = Column(String)
royal = relationship("Royal", backref="telegram") @declared_attr
def tg_id(self):
return Column(BigInteger, primary_key=True)
@declared_attr
def first_name(self):
return Column(String)
@declared_attr
def last_name(self):
return Column(String)
@declared_attr
def username(self):
return Column(String)
@declared_attr
def royal(self):
return relationship("Royal", backref="telegram")
def __repr__(self): def __repr__(self):
return f"<Telegram {str(self)}>" return f"<Telegram {str(self)}>"

View file

@ -20,6 +20,7 @@ class Call:
# These parameters / methods should be overridden # These parameters / methods should be overridden
interface_name = NotImplemented interface_name = NotImplemented
interface_obj = NotImplemented interface_obj = NotImplemented
interface_prefix = NotImplemented
alchemy: "Alchemy" = NotImplemented alchemy: "Alchemy" = NotImplemented
async def reply(self, text: str): async def reply(self, text: str):