Re-implement extra commands and remove !reload

master
Nikola Forró 6 years ago
parent be7187d3a7
commit 38b8e04acf

@ -7,13 +7,14 @@ using [HTTP API](https://gitea.brno.mraveniste.cc/turbotraktor/ladylilia.com/src
* `!lastquote` - posts the most recent quote * `!lastquote` - posts the most recent quote
* `!findquote PATTERN` - searches for quotes matching `PATTERN` and in case multiple matches are found, posts one of them randomly * `!findquote PATTERN` - searches for quotes matching `PATTERN` and in case multiple matches are found, posts one of them randomly
- `PATTERN` has to be at least 3 characters long and it can be enclosed in double quotes in case it contains spaces - `PATTERN` has to be at least 3 characters long
* `!bellagram`, `!bellapics`, `!instabella`, `!instagram` - posts a link to a random Instagram picture of Bella * `!bellagram`, `!bellapics`, `!instabella`, `!instagram` - posts a link to a random Instagram picture of Bella
* `!yt QUERY` - queries Lady Lilia's Youtube channel and posts a link to the most relevant result * `!yt QUERY` - queries Lady Lilia's Youtube channel and posts a link to the most relevant result
- `QUERY` can contain `|` (logical **or**) and `-` (logical **not**) operators, for example: `!yt oblivion -nehrim` - `QUERY` can contain `|` (logical **or**) and `-` (logical **not**) operators, for example: `!yt oblivion -nehrim`
* `!clip PATTERN` - searches for Twitch clips matching `PATTERN` and in case multiple matches are found, posts one of them randomly * `!clip PATTERN` - searches for Twitch clips matching `PATTERN` and in case multiple matches are found, posts one of them randomly
- `PATTERN` has to be at least 3 characters long and it can be enclosed in double quotes in case it contains spaces - `PATTERN` has to be at least 3 characters long
### Mod-only commands
* `!syncquotes` - performs synchronization of the quotes database with Twitch VODs * `!syncquotes` - performs synchronization of the quotes database with Twitch VODs
- only master user is allowed to use this command * `!command set COMMAND TEXT` - adds a new extra command or updates an existing one
* `!reload` - reloads the list of extra commands from the config file * `!command unset COMMAND` - removes an existing extra command
- only master user is allowed to use this command

@ -4,6 +4,7 @@ import logging
import os import os
from commands import Commands from commands import Commands
from extracommands import ExtraCommands
from clients.discord import DiscordClient from clients.discord import DiscordClient
from clients.twitch import TwitchClient from clients.twitch import TwitchClient
@ -37,19 +38,29 @@ def main():
commands = Commands(config, commands_logger) commands = Commands(config, commands_logger)
extra_commands = ExtraCommands()
async def monitor_extra_commands():
while True:
try:
extra_commands.reload_if_needed()
except Exception as e:
commands_logger.info('Exception', exc_info=e)
await asyncio.sleep(TIMEOUT)
async def run_twitch_client(): async def run_twitch_client():
twitch_client = TwitchClient(config, twitch_logger, commands) twitch_client = TwitchClient(config, twitch_logger, commands, extra_commands)
twitch_client.connect_() twitch_client.connect_()
while True: while True:
try: try:
twitch_client.process_data() twitch_client.process_data()
await asyncio.sleep(TIMEOUT)
except Exception as e: except Exception as e:
twitch_logger.info('Exception', exc_info=e) twitch_logger.info('Exception', exc_info=e)
await asyncio.sleep(TIMEOUT)
async def run_discord_client(): async def run_discord_client():
while True: while True:
discord_client = DiscordClient(config, discord_logger, commands) discord_client = DiscordClient(config, discord_logger, commands, extra_commands)
try: try:
await discord_client.start_() await discord_client.start_()
except Exception as e: except Exception as e:
@ -58,6 +69,7 @@ def main():
loop = asyncio.get_event_loop() loop = asyncio.get_event_loop()
try: try:
asyncio.ensure_future(monitor_extra_commands())
asyncio.ensure_future(run_twitch_client()) asyncio.ensure_future(run_twitch_client())
asyncio.ensure_future(run_discord_client()) asyncio.ensure_future(run_discord_client())
loop.run_forever() loop.run_forever()

@ -31,10 +31,11 @@ def cooldown(retries, timeout, failure):
class DiscordClient(discord.Client): class DiscordClient(discord.Client):
def __init__(self, config, logger, commands): def __init__(self, config, logger, commands, extra_commands):
self.config = config self.config = config
self.logger = logger self.logger = logger
self.commands = commands self.commands = commands
self.extra_commands = extra_commands
self.supported_commands = [ self.supported_commands = [
(re.compile(r'^!lastquote$'), self._do_lastquote), (re.compile(r'^!lastquote$'), self._do_lastquote),
(re.compile(r'^!findquote\s+(?P<q>")?(?P<filter>.+)(?(q)")$'), self._do_findquote), (re.compile(r'^!findquote\s+(?P<q>")?(?P<filter>.+)(?(q)")$'), self._do_findquote),
@ -57,7 +58,7 @@ class DiscordClient(discord.Client):
m = pattern.match(message.content) m = pattern.match(message.content)
if m: if m:
await action(server, message.author.id, message, **m.groupdict()) await action(server, message.author.id, message, **m.groupdict())
for cmd, resp in self.config['Extra Commands'].items(): for cmd, resp in self.extra_commands.items():
if cmd == message.content: if cmd == message.content:
await self.send_message(message.channel, resp) await self.send_message(message.channel, resp)

@ -33,10 +33,11 @@ QUOTE_REMOVED_PATTERN = re.compile(r'''^
class TwitchClient(irc.bot.SingleServerIRCBot): class TwitchClient(irc.bot.SingleServerIRCBot):
def __init__(self, config, logger, commands): def __init__(self, config, logger, commands, extra_commands):
self.config = config self.config = config
self.logger = logger self.logger = logger
self.commands = commands self.commands = commands
self.extra_commands = extra_commands
self.patterns = [ self.patterns = [
(QUOTE_ADDED_PATTERN, self._add_quote), (QUOTE_ADDED_PATTERN, self._add_quote),
(QUOTE_EDITED_PATTERN, self._edit_quote), (QUOTE_EDITED_PATTERN, self._edit_quote),
@ -49,7 +50,9 @@ class TwitchClient(irc.bot.SingleServerIRCBot):
(re.compile(r'^!(bella(gram|pics)|insta(gram|bella))$'), self._do_bellagram), (re.compile(r'^!(bella(gram|pics)|insta(gram|bella))$'), self._do_bellagram),
(re.compile(r'^!yt\s+(?P<q>")?(?P<query>.+)(?(q)")$'), self._do_yt), (re.compile(r'^!yt\s+(?P<q>")?(?P<query>.+)(?(q)")$'), self._do_yt),
(re.compile(r'^!clip\s+(?P<q>")?(?P<filter>.+)(?(q)")$'), self._do_clip), (re.compile(r'^!clip\s+(?P<q>")?(?P<filter>.+)(?(q)")$'), self._do_clip),
(re.compile(r'^!reload$'), self._do_reload), (re.compile(r'^!command\s+set\s+(?P<q1>")?(?P<cmd>.+?)(?(q1)")\s+'
r'(?P<q2>")?(?P<resp>.+)(?(q2)")$'), self._do_command_set),
(re.compile(r'^!command\s+unset\s+(?P<q>")?(?P<cmd>.+)(?(q)")$'), self._do_command_unset),
] ]
server = self.config['IRC'].get('server') server = self.config['IRC'].get('server')
port = self.config['IRC'].getint('port') port = self.config['IRC'].getint('port')
@ -82,6 +85,10 @@ class TwitchClient(irc.bot.SingleServerIRCBot):
def on_whisper(self, connection, event): def on_whisper(self, connection, event):
self._process_message(connection, event) self._process_message(connection, event)
def _is_mod(self, tags):
badges = [b.split('/')[0] for b in tags['badges'].split(',')]
return bool(set(badges).intersection(['admin', 'broadcaster', 'moderator']))
def _send_response(self, connection, event, msg): def _send_response(self, connection, event, msg):
if event.target.startswith('#'): if event.target.startswith('#'):
connection.privmsg(event.target, msg) connection.privmsg(event.target, msg)
@ -97,7 +104,7 @@ class TwitchClient(irc.bot.SingleServerIRCBot):
m = pattern.match(message) m = pattern.match(message)
if m: if m:
action(tags, send_response, **m.groupdict()) action(tags, send_response, **m.groupdict())
for cmd, resp in self.config['Extra Commands'].items(): for cmd, resp in self.extra_commands.items():
if cmd == message: if cmd == message:
send_response(resp) send_response(resp)
@ -149,9 +156,8 @@ class TwitchClient(irc.bot.SingleServerIRCBot):
send_response('One time, Lilia said this... #{id}: "{text}" [{game}] [{date}]'.format(**quote)) send_response('One time, Lilia said this... #{id}: "{text}" [{game}] [{date}]'.format(**quote))
def _do_syncquotes(self, tags, send_response, **kwargs): def _do_syncquotes(self, tags, send_response, **kwargs):
master_user_id = self.config['Twitch'].getint('master_user_id') if not self._is_mod(tags):
if int(tags['user-id']) != master_user_id: send_response('Sorry @{0}, you are not allowed to do this'.format(tags['display-name']))
respond('Sorry @{0}, you are not allowed to do this'.format(tags['display-name']))
return return
try: try:
messages = self.commands.get_quote_messages() messages = self.commands.get_quote_messages()
@ -163,6 +169,7 @@ class TwitchClient(irc.bot.SingleServerIRCBot):
m = pattern.match(message) m = pattern.match(message)
if m: if m:
action(tags, send_response, **m.groupdict()) action(tags, send_response, **m.groupdict())
send_response('Sync finished, @{0}'.format(tags['display-name']))
def _do_bellagram(self, tags, send_response, **kwargs): def _do_bellagram(self, tags, send_response, **kwargs):
try: try:
@ -188,10 +195,28 @@ class TwitchClient(irc.bot.SingleServerIRCBot):
else: else:
send_response('{0}: {1}'.format(result['title'], result['url'])) send_response('{0}: {1}'.format(result['title'], result['url']))
def _do_reload(self, tags, send_response, **kwargs): def _do_command_set(self, tags, send_response, cmd, resp, **kwargs):
master_user_id = self.config['Twitch'].getint('master_user_id') if not self._is_mod(tags):
if int(tags['user-id']) != master_user_id: send_response('Sorry @{0}, you are not allowed to do this'.format(tags['display-name']))
respond('Sorry @{0}, you are not allowed to do this'.format(tags['display-name']))
return return
self.config.remove_section('Extra Commands') try:
self.config.read('settings.cfg') self.extra_commands[cmd] = resp
self.extra_commands.save()
except Exception as e:
self.logger.error('Failed to update extra commands: %s', e)
send_response('Sorry @{0}, failed to set the command'.format(tags['display-name']))
else:
send_response('Command {0} set, @{1}'.format(cmd, tags['display-name']))
def _do_command_unset(self, tags, send_response, cmd, **kwargs):
if not self._is_mod(tags):
send_response('Sorry @{0}, you are not allowed to do this'.format(tags['display-name']))
return
try:
self.extra_commands.pop(cmd, None)
self.extra_commands.save()
except Exception as e:
self.logger.error('Failed to update extra commands: %s', e)
send_response('Sorry @{0}, failed to unset the command'.format(tags['display-name']))
else:
send_response('Command {0} unset, @{1}'.format(cmd, tags['display-name']))

@ -6,5 +6,6 @@ services:
context: . context: .
volumes: volumes:
- /bot/settings.cfg:/bot/settings.cfg - /bot/settings.cfg:/bot/settings.cfg
- /bot/extra-commands.json:/bot/extra-commands.json
environment: environment:
- DEBUG=0 - DEBUG=0

@ -0,0 +1,27 @@
import json
import os
FILENAME = 'extra-commands.json'
class ExtraCommands(dict):
def __init__(self):
self.last_mtime = 0
def reload(self):
with open(FILENAME) as f:
commands = json.load(f)
self.clear()
self.update(commands)
def reload_if_needed(self):
mtime = os.stat(FILENAME).st_mtime
if mtime > self.last_mtime:
self.last_mtime = mtime
self.reload()
def save(self):
with open(FILENAME, 'w') as f:
json.dump(self, f, indent=4, sort_keys=True)

@ -10,8 +10,6 @@ token = __DISCORD_TOKEN__
[Twitch] [Twitch]
cache_api_url = https://ladylilia.com/twitch-cache/api cache_api_url = https://ladylilia.com/twitch-cache/api
token = oauth:__TWITCH_OAUTH_TOKEN__ token = oauth:__TWITCH_OAUTH_TOKEN__
# nikola_f
master_user_id = 210957066
[Instagram] [Instagram]
username = lilialovesgames username = lilialovesgames
@ -26,6 +24,3 @@ channel_ids = UC5970RJMoEcRNZl0MNp8tlQ,UCHNjavmkFUf2n0uzWSgj16w
api_url = https://ladylilia.com/quotes/api api_url = https://ladylilia.com/quotes/api
api_key = __QUOTES_API_KEY__ api_key = __QUOTES_API_KEY__
act_as_proxy = true act_as_proxy = true
[Extra Commands]
;!command = Response Text

Loading…
Cancel
Save