WIP mostly running again

This commit is contained in:
John Lancaster
2023-05-24 21:24:56 -05:00
parent e46c3bd65b
commit 61e3065a87
7 changed files with 214 additions and 197 deletions

View File

@@ -2,7 +2,8 @@ import logging
import random
import re
from nextcord import Message, Client
# from nextcord import Message, Client
from discord import Message, Client
LOGGER = logging.getLogger(__name__)

View File

@@ -2,8 +2,9 @@ import logging
import re
from random import choice
from nextcord import Client, Message
from nextcord import utils
# from nextcord import Client, Message
# from nextcord import utils
from discord import Client, Message, utils
from . import base, helpers

View File

@@ -1,165 +1,168 @@
import asyncio
import logging
import re
import sqlite3
from datetime import datetime, timedelta
from pathlib import Path
from typing import List
import pandas as pd
from nextcord import Client, Message, TextChannel
from nextcord import RawReactionActionEvent, Emoji
from nextcord import utils
from . import jokes
from .reactions import ReactionData
LIL_STINKY_ID = 704043422276780072
LOGGER = logging.getLogger(__name__)
class Kwaylon(Client):
def __init__(self, limit: int = 5000, days: int = 30, db_path: Path = None, *args, **kwargs):
super().__init__(*args, **kwargs)
if db_path is None:
self.db_path = Path(__file__).parents[2] / 'data' / 'messages.db'
else:
self.db_path = db_path
self.limit, self.days = limit, days
self.jokes = list(jokes.collect_jokes())
self.lock = asyncio.Lock()
self.most_regex = re.compile('^most\s*(?P<emoji>\S+)', re.IGNORECASE)
def text_channels(self) -> List[TextChannel]:
return [chan for chan in self.get_all_channels() if isinstance(chan, TextChannel)]
def robotics_facility(self) -> TextChannel:
for chan in self.text_channels():
if chan.name == 'robotics-facility' and chan.guild.name == 'Family Dinner':
return chan
def kaylon_emoji(self) -> Emoji:
return utils.get(self.emojis, name='kaylon')
async def handle_ready(self):
async def alive():
channel: TextChannel = self.robotics_facility()
await channel.send('https://tenor.com/view/terminator-im-back-gif-19144173')
await channel.send(self.kaylon_emoji())
# await alive()
try:
self.data = ReactionData(self.db_path)
LOGGER.info(f'{self.data.row_count():d} reactions in {self.db_path}')
except sqlite3.Error as e:
LOGGER.exception(e)
LOGGER.error(f'self.db_path: {self.db_path}')
async def handle_message(self, message: Message):
if message.author != self.user:
await self.read_command(message)
await self.respond_to_joke(message)
await self.respond_to_emoji(message)
async def read_command(self, message: Message):
for mention in message.mentions:
if mention.id == self.user.id and 'read' in message.content:
days = get_days(message.content) or self.days
await self.data.scan_messages(client=self, limit=self.limit, days=days)
async def respond_to_emoji(self, message: Message):
if (most_match := self.most_regex.match(message.content)):
emoji_ref = most_match.group('emoji')
emoji_name = get_emoji_name(emoji_ref)
LOGGER.info(f'Most {emoji_name}')
async with message.channel.typing():
with self.data.connect() as con:
days = get_days(message.content) or 14
if days >= 1000:
await message.reply(
f'https://tenor.com/view/i-hate-you-anakin-darth-vader-vader-star-wars-gif-13071041'
)
return
df = self.data.read_emoji(emoji_name, con=con, days=days)
con.close()
if df.shape[0] > 0:
LOGGER.info(f'{df.shape[0]} messages with {emoji_ref} after filtering')
if 'leaderboard' in message.content:
LOGGER.info(f'Building leaderboard')
res = f'{emoji_ref} totals, past {days} days\n'
if (board := await self.leaderboard(df)) is not None:
res += board
await message.reply(res)
else:
if len(message.mentions) > 0:
df = df[df['auth_id'].isin([m.id for m in message.mentions])]
most = df.sort_values('count').iloc[-1]
msg = await self.fetch_message(most)
await message.reply(f'Most {emoji_ref} in past {days} days\n{msg.jump_url}')
# else:
# await message.reply(f"NObody (in the past {days} days)...gah, leave me alone!")
LOGGER.info(f'Done')
async def respond_to_joke(self, message: Message):
for joke in self.jokes:
if (joke_match := joke.scan(message)):
LOGGER.info(f'{joke.__class__.__name__} detected: {message.content}, {joke_match.group()}')
await joke.respond(message, self, joke_match)
async def leaderboard(self, df: pd.DataFrame) -> str:
df = df.groupby('auth_id').sum()
counts = df['count'].sort_values(ascending=False)
counts.index = [(await self.fetch_user(idx)).display_name for idx in counts.index]
width = max([len(str(s)) for s in counts.index])
res = '\n'.join(
f"`{str(name).ljust(width + 1)}with {cnt:<2.0f} total`"
for name, cnt in counts.iteritems()
)
return res
async def handle_raw_reaction(self, payload: RawReactionActionEvent):
LOGGER.info(payload)
guild = await self.fetch_guild(payload.guild_id)
channel = await guild.fetch_channel(payload.channel_id)
message = await channel.fetch_message(payload.message_id)
async with self.lock:
with self.data.connect() as con:
self.data.add_reactions_from_message(message, con)
con.close()
async def fetch_message(self, row: pd.Series):
guild = await self.fetch_guild(row['guild_id'])
channel = await guild.fetch_channel(row['channel_id'])
return await channel.fetch_message(row['msg_id'])
async def scan_messages(self, **kwargs):
async with self.lock:
await self.data.scan_messages(client=self, **kwargs)
def get_emoji_name(string: str) -> str:
if (m := re.search('<:(?P<name>\w+):(?P<id>\d+)>', string)):
string = m.group('name')
return string.lower().strip()
def get_days(input_str):
if (m := re.search('(?P<days>\d+) days', input_str)):
return int(m.group('days'))
import asyncio
import logging
import re
import sqlite3
from pathlib import Path
from typing import List
import pandas as pd
# from nextcord import Client, Message, TextChannel
# from nextcord import RawReactionActionEvent, Emoji
# from nextcord import utils
from discord import Client, Message, TextChannel
from discord import RawReactionActionEvent, Emoji
from discord import utils
from . import jokes
from .reactions import ReactionData
LIL_STINKY_ID = 704043422276780072
LOGGER = logging.getLogger(__name__)
class Kwaylon(Client):
def __init__(self, limit: int = 5000, days: int = 30, db_path: Path = None, *args, **kwargs):
super().__init__(*args, **kwargs)
if db_path is None:
self.db_path = Path(__file__).parents[2] / 'data' / 'messages.db'
else:
self.db_path = db_path
self.limit, self.days = limit, days
self.jokes = list(jokes.collect_jokes())
self.lock = asyncio.Lock()
self.most_regex = re.compile('^most\s*(?P<emoji>\S+)', re.IGNORECASE)
def text_channels(self) -> List[TextChannel]:
return [chan for chan in self.get_all_channels() if isinstance(chan, TextChannel)]
def robotics_facility(self) -> TextChannel:
for chan in self.text_channels():
if chan.name == 'robotics-facility' and chan.guild.name == 'Family Dinner':
return chan
def kaylon_emoji(self) -> Emoji:
return utils.get(self.emojis, name='kaylon')
async def handle_ready(self):
async def alive():
channel: TextChannel = self.robotics_facility()
await channel.send('https://tenor.com/view/terminator-im-back-gif-19144173')
await channel.send(self.kaylon_emoji())
# await alive()
try:
self.data = ReactionData(self.db_path)
LOGGER.info(f'{self.data.row_count():d} reactions in {self.db_path}')
except sqlite3.Error as e:
LOGGER.exception(e)
LOGGER.error(f'self.db_path: {self.db_path}')
async def handle_message(self, message: Message):
if message.author != self.user:
await self.read_command(message)
await self.respond_to_joke(message)
await self.respond_to_emoji(message)
async def read_command(self, message: Message):
for mention in message.mentions:
if mention.id == self.user.id and 'read' in message.content:
days = get_days(message.content) or self.days
await self.data.scan_messages(client=self, limit=self.limit, days=days)
async def respond_to_emoji(self, message: Message):
if (most_match := self.most_regex.match(message.content)):
emoji_ref = most_match.group('emoji')
emoji_name = get_emoji_name(emoji_ref)
LOGGER.info(f'Most {emoji_name}')
async with message.channel.typing():
with self.data.connect() as con:
days = get_days(message.content) or 14
if days >= 1000:
await message.reply(
f'https://tenor.com/view/i-hate-you-anakin-darth-vader-vader-star-wars-gif-13071041'
)
return
df = self.data.read_emoji(emoji_name, con=con, days=days)
con.close()
if df.shape[0] > 0:
LOGGER.info(f'{df.shape[0]} messages with {emoji_ref} after filtering')
if 'leaderboard' in message.content:
LOGGER.info(f'Building leaderboard')
res = f'{emoji_ref} totals, past {days} days\n'
if (board := await self.leaderboard(df)) is not None:
res += board
await message.reply(res)
else:
if len(message.mentions) > 0:
df = df[df['auth_id'].isin([m.id for m in message.mentions])]
most = df.sort_values('count').iloc[-1]
msg = await self.fetch_message(most)
await message.reply(f'Most {emoji_ref} in past {days} days\n{msg.jump_url}')
# else:
# await message.reply(f"NObody (in the past {days} days)...gah, leave me alone!")
LOGGER.info(f'Done')
async def respond_to_joke(self, message: Message):
for joke in self.jokes:
if (joke_match := joke.scan(message)):
LOGGER.info(f'{joke.__class__.__name__} detected: {message.content}, {joke_match.group()}')
await joke.respond(message, self, joke_match)
async def leaderboard(self, df: pd.DataFrame) -> str:
df = df.groupby('auth_id').sum()
counts = df['count'].sort_values(ascending=False)
counts.index = [(await self.fetch_user(idx)).display_name for idx in counts.index]
width = max([len(str(s)) for s in counts.index])
res = '\n'.join(
f"`{str(name).ljust(width + 1)}with {cnt:<2.0f} total`"
for name, cnt in counts.iteritems()
)
return res
async def handle_raw_reaction(self, payload: RawReactionActionEvent):
LOGGER.info(payload)
guild = await self.fetch_guild(payload.guild_id)
channel = await guild.fetch_channel(payload.channel_id)
message = await channel.fetch_message(payload.message_id)
async with self.lock:
with self.data.connect() as con:
self.data.add_reactions_from_message(message, con)
con.close()
async def fetch_message(self, row: pd.Series):
guild = await self.fetch_guild(row['guild_id'])
channel = await guild.fetch_channel(row['channel_id'])
return await channel.fetch_message(row['msg_id'])
async def scan_messages(self, **kwargs):
async with self.lock:
await self.data.scan_messages(client=self, **kwargs)
def get_emoji_name(string: str) -> str:
if (m := re.search('<:(?P<name>\w+):(?P<id>\d+)>', string)):
string = m.group('name')
return string.lower().strip()
def get_days(input_str):
if (m := re.search('(?P<days>\d+) days', input_str)):
return int(m.group('days'))

View File

@@ -2,9 +2,12 @@ import logging
from datetime import datetime, timedelta
import pandas as pd
from nextcord import Client, Message, Reaction
from nextcord import TextChannel
from nextcord.utils import AsyncIterator
from discord import Client, Message, Reaction, TextChannel
# from nextcord import Client, Message, Reaction
# from nextcord import TextChannel
# from nextcord.utils import AsyncIterator
LOGGER = logging.getLogger(__name__)
@@ -15,7 +18,7 @@ async def message_gen(client: Client,
days: int = None,
after: datetime = None,
around: datetime = None,
oldest_first: bool = True) -> AsyncIterator[Message]:
oldest_first: bool = True):
if days is not None:
after = (datetime.today() - timedelta(days=days)).astimezone()
@@ -38,7 +41,8 @@ async def message_gen(client: Client,
async for msg in channel.history(**kwargs):
yield msg
for thread in channel.threads:
LOGGER.info(f'Thread: {channel.category}: {channel.name}: {thread.name}')
LOGGER.info(
f'Thread: {channel.category}: {channel.name}: {thread.name}')
async for msg in thread.history(**kwargs):
yield msg
else:
@@ -60,7 +64,7 @@ def reaction_dict(reaction: Reaction):
}
async def reaction_gen(client: Client, **kwargs) -> AsyncIterator[Reaction]:
async def reaction_gen(client: Client, **kwargs):
async for msg in message_gen(client=client, **kwargs):
for reaction in msg.reactions:
yield reaction_dict(reaction)

View File

@@ -5,7 +5,8 @@ from datetime import datetime, timedelta
from pathlib import Path
import pandas as pd
from nextcord import Message, Client
# from nextcord import Message, Client
from discord import Message, Client
from .msg import reaction_dict, message_gen

View File

@@ -1,47 +1,53 @@
#!/usr/bin/env python3
import logging
import os
import nextcord as discord
from discord import Intents, Message, RawReactionActionEvent
from dotenv import load_dotenv
from nextcord import RawReactionActionEvent
from rich.highlighter import NullHighlighter
from rich.logging import RichHandler
from kwaylon import Kwaylon
if __name__ == '__main__':
import logging
logging.basicConfig(
level=logging.DEBUG,
# https://docs.python.org/3/library/logging.html#logrecord-attributes
format='[magenta]%(name)s[/] [cyan]%(funcName)s[/] %(message)s',
datefmt='%Y-%m-%d %I:%M:%S %p',
handlers=[
RichHandler(
highlighter=NullHighlighter(),
markup=True,
rich_tracebacks=True,
tracebacks_suppress=['pandas'],
)
]
)
logging.basicConfig(level=logging.INFO)
client = Kwaylon()
for handler in logging.getLogger('discord.client').handlers:
print(handler)
intents = Intents.default()
intents.message_content = True
client = Kwaylon(intents=intents)
@client.event
async def on_ready():
await client.handle_ready()
# await client.data.scan_messages(
# client=client,
# limit=50,
# # days=7,
# )
# chan = await client.fetch_channel(690588413543579649)
# msg = await chan.fetch_message(936684979654623293)
# logging.info(f'Msg: {msg.clean_content}')
# await msg.reply(f'https://tenor.com/view/i-will-orange-county-jack-black-nodding-nod-gif-4984565')
@client.event
async def on_message(message: discord.Message):
async def on_message(message: Message):
await client.handle_message(message)
@client.event
async def on_raw_reaction_add(payload: RawReactionActionEvent):
await client.handle_raw_reaction(payload)
@client.event
async def on_raw_reaction_remove(payload: RawReactionActionEvent):
await client.handle_raw_reaction(payload)
load_dotenv()
client.run(os.getenv('DISCORD_TOKEN'))