add event command

pull/1/head
qwewqa 4 years ago
parent 9c8665e312
commit 550b63f60c
  1. 1
      main.py
  2. 95
      miyu_bot/commands/cogs/event.py
  3. 95
      miyu_bot/commands/cogs/music.py
  4. 41
      miyu_bot/commands/common/emoji.py
  5. 2
      miyu_bot/commands/common/formatting.py
  6. 13
      miyu_bot/commands/common/fuzzy_matching.py
  7. 29
      miyu_bot/commands/common/reaction_message.py

@ -15,6 +15,7 @@ bot = commands.Bot(command_prefix='!', case_insensitive=True)
asset_manager = AssetManager('assets') asset_manager = AssetManager('assets')
bot.load_extension('miyu_bot.commands.cogs.card') bot.load_extension('miyu_bot.commands.cogs.card')
bot.load_extension('miyu_bot.commands.cogs.event')
bot.load_extension('miyu_bot.commands.cogs.music') bot.load_extension('miyu_bot.commands.cogs.music')
bot.load_extension('miyu_bot.commands.cogs.utility') bot.load_extension('miyu_bot.commands.cogs.utility')

@ -0,0 +1,95 @@
import datetime
import logging
import discord
from d4dj_utils.master.event_master import EventMaster
from discord.ext import commands
from main import asset_manager
from miyu_bot.commands.common.emoji import attribute_emoji_by_id, unit_emoji_by_id, parameter_bonus_emoji_by_id, \
event_point_emoji
from miyu_bot.commands.common.formatting import format_info
from miyu_bot.commands.common.fuzzy_matching import FuzzyMap, romanize
class Event(commands.Cog):
def __init__(self, bot: commands.Bot):
self.bot = bot
self.logger = logging.getLogger(__name__)
self.events = FuzzyMap(
lambda e: e.start_datetime < datetime.datetime.now(datetime.timezone.utc) + datetime.timedelta(hours=-8)
)
for e in asset_manager.event_master.values():
self.events[e.name] = e
@commands.command(name='event',
aliases=['ev'],
description='Finds the event with the given name.',
help='!event pkcooking')
async def event(self, ctx: commands.Context, *, arg: str):
self.logger.info(f'Searching for event "{arg}".')
event: EventMaster
try:
event = asset_manager.event_master[int(arg)]
if event not in self.events.values():
event = self.events[arg]
except (ValueError, KeyError):
event = self.events[arg]
if not event:
msg = f'Failed to find event "{arg}".'
await ctx.send(msg)
self.logger.info(msg)
return
self.logger.info(f'Found event "{event}" ({romanize(event.name)}).')
logo = discord.File(event.logo_path, filename='logo.png')
embed = discord.Embed(title=event.name)
embed.set_thumbnail(url=f'attachment://logo.png')
embed.add_field(name='Dates',
value=format_info({
'Start': event.start_datetime,
'Close': event.reception_close_datetime,
'Rank Fix': event.rank_fix_start_datetime,
'Results': event.result_announcement_datetime,
'End': event.end_datetime,
'Story Unlock': event.story_unlock_datetime,
'Status': event.state().name,
}),
inline=False)
embed.add_field(name='Event Type',
value=event.event_type.name,
inline=True)
embed.add_field(name='Bonus Characters',
value='\n'.join(
f'{self.bot.get_emoji(unit_emoji_by_id[char.unit_id])} {char.full_name_english}'
for char in event.bonus.characters
),
inline=True)
embed.add_field(name='Bonus Attribute',
value=f'{self.bot.get_emoji(attribute_emoji_by_id[event.bonus.attribute_id])} '
f'{event.bonus.attribute.en_name.capitalize()}' if event.bonus.attribute else 'None',
inline=True)
embed.add_field(name='Point Bonus',
value=format_info({
'Attribute': f'{self.bot.get_emoji(event_point_emoji)} +{event.bonus.attribute_match_point_bonus_value}%' if event.bonus.attribute_match_point_bonus_value else 'None',
'Character': f'{self.bot.get_emoji(event_point_emoji)} +{event.bonus.character_match_point_bonus_value}%' if event.bonus.character_match_point_bonus_value else 'None',
'Both': f'{self.bot.get_emoji(event_point_emoji)} +{event.bonus.all_match_point_bonus_value}%' if event.bonus.all_match_point_bonus_value else 'None',
}),
inline=True)
embed.add_field(name='Parameter Bonus',
value=format_info({
'Attribute': f'{self.bot.get_emoji(parameter_bonus_emoji_by_id[event.bonus.attribute_match_parameter_bonus_id])} +{event.bonus.attribute_match_parameter_bonus_value}%' if event.bonus.attribute_match_parameter_bonus_value else 'None',
'Character': f'{self.bot.get_emoji(parameter_bonus_emoji_by_id[event.bonus.character_match_parameter_bonus_id])} +{event.bonus.attribute_match_parameter_bonus_value}%' if event.bonus.attribute_match_parameter_bonus_value else 'None',
'Both': f'{self.bot.get_emoji(parameter_bonus_emoji_by_id[event.bonus.all_match_parameter_bonus_id])} +{event.bonus.all_match_parameter_bonus_value}%' if event.bonus.all_match_parameter_bonus_value else 'None',
}),
inline=True)
await ctx.send(files=[logo], embed=embed)
def setup(bot):
bot.add_cog(Event(bot))

@ -1,5 +1,6 @@
import asyncio import asyncio
import logging import logging
from typing import Optional, Tuple
import discord import discord
from d4dj_utils.master.chart_master import ChartDifficulty, ChartMaster from d4dj_utils.master.chart_master import ChartDifficulty, ChartMaster
@ -8,6 +9,8 @@ from d4dj_utils.master.music_master import MusicMaster
from discord.ext import commands from discord.ext import commands
from main import asset_manager from main import asset_manager
from miyu_bot.commands.common.emoji import difficulty_emoji_id
from miyu_bot.commands.common.formatting import format_info
from miyu_bot.commands.common.fuzzy_matching import romanize, FuzzyMap from miyu_bot.commands.common.fuzzy_matching import romanize, FuzzyMap
from miyu_bot.commands.common.reaction_message import make_tabbed_message from miyu_bot.commands.common.reaction_message import make_tabbed_message
@ -16,13 +19,9 @@ class Music(commands.Cog):
def __init__(self, bot: commands.Bot): def __init__(self, bot: commands.Bot):
self.bot = bot self.bot = bot
self.logger = logging.getLogger(__name__) self.logger = logging.getLogger(__name__)
self.music = self.get_music() self.music = FuzzyMap(lambda m: m.is_released)
def get_music(self):
music = FuzzyMap(lambda m: m.is_released)
for m in asset_manager.music_master.values(): for m in asset_manager.music_master.values():
music[f'{m.name} {m.special_unit_name}'] = m self.music[f'{m.name} {m.special_unit_name}'] = m
return music
difficulty_names = { difficulty_names = {
'expert': ChartDifficulty.Expert, 'expert': ChartDifficulty.Expert,
@ -39,10 +38,6 @@ class Music(commands.Cog):
'es': ChartDifficulty.Easy, 'es': ChartDifficulty.Easy,
} }
@staticmethod
def format_info(info_entries: dict):
return '\n'.join(f'{k}: {v}' for k, v in info_entries.items() if v)
@commands.command(name='song', @commands.command(name='song',
aliases=['music'], aliases=['music'],
description='Finds the song with the given name.', description='Finds the song with the given name.',
@ -50,13 +45,14 @@ class Music(commands.Cog):
async def song(self, ctx: commands.Context, *, arg: str): async def song(self, ctx: commands.Context, *, arg: str):
self.logger.info(f'Searching for song "{arg}".') self.logger.info(f'Searching for song "{arg}".')
song: MusicMaster = self.music[arg] song = self.get_song(arg)
if not song: if not song:
msg = f'Failed to find song "{arg}".' msg = f'Failed to find song "{arg}".'
await ctx.send(msg) await ctx.send(msg)
self.logger.info(msg) self.logger.info(msg)
return return
self.logger.info(f'Found "{song}" ({romanize(song.name)[1]}).') self.logger.info(f'Found song "{song}" ({romanize(song.name)}).')
thumb = discord.File(song.jacket_path, filename='jacket.png') thumb = discord.File(song.jacket_path, filename='jacket.png')
@ -81,10 +77,10 @@ class Music(commands.Cog):
} }
embed.add_field(name='Artist', embed.add_field(name='Artist',
value=self.format_info(artist_info), value=format_info(artist_info),
inline=False) inline=False)
embed.add_field(name='Info', embed.add_field(name='Info',
value=self.format_info(music_info), value=format_info(music_info),
inline=False) inline=False)
await ctx.send(files=[thumb], embed=embed) await ctx.send(files=[thumb], embed=embed)
@ -96,35 +92,28 @@ class Music(commands.Cog):
async def chart(self, ctx: commands.Context, *, arg: str): async def chart(self, ctx: commands.Context, *, arg: str):
self.logger.info(f'Searching for chart "{arg}".') self.logger.info(f'Searching for chart "{arg}".')
split_args = arg.split() name, difficulty = self.parse_chart_args(arg)
song = self.get_song(name)
difficulty = ChartDifficulty.Expert
if len(split_args) >= 2:
final_word = split_args[-1]
if final_word in self.difficulty_names:
difficulty = self.difficulty_names[final_word]
arg = ''.join(split_args[:-1])
song: MusicMaster = self.music[arg]
if not song: if not song:
msg = f'Failed to find chart "{arg}".' msg = f'Failed to find chart "{name}".'
await ctx.send(msg) await ctx.send(msg)
self.logger.info(msg) self.logger.info(msg)
return return
self.logger.info(f'Found "{song}" ({romanize(song.name)[1]}).') self.logger.info(f'Found song "{song}" ({romanize(song.name)}).')
embeds, files = self.get_chart_embed_info(song) embeds, files = self.get_chart_embed_info(song)
message = await ctx.send(files=files, embed=embeds[difficulty - 1]) message = await ctx.send(files=files, embed=embeds[difficulty - 1])
reaction_emote_ids = [ reaction_emoji_ids = [
790050636568723466, 790050636568723466,
790050636489555998, 790050636489555998,
790050636548276252, 790050636548276252,
790050636225052694, 790050636225052694,
] ]
asyncio.ensure_future(make_tabbed_message(ctx, message, reaction_emote_ids, embeds)) asyncio.ensure_future(make_tabbed_message(ctx, message, reaction_emoji_ids, embeds))
@commands.command(name='sections', @commands.command(name='sections',
aliases=['mixes'], aliases=['mixes'],
@ -133,18 +122,11 @@ class Music(commands.Cog):
async def sections(self, ctx: commands.Context, *, arg: str): async def sections(self, ctx: commands.Context, *, arg: str):
self.logger.info(f'Searching for chart sections "{arg}".') self.logger.info(f'Searching for chart sections "{arg}".')
split_args = arg.split() name, difficulty = self.parse_chart_args(arg)
song = self.get_song(name)
difficulty = ChartDifficulty.Expert
if len(split_args) >= 2:
final_word = split_args[-1]
if final_word in self.difficulty_names:
difficulty = self.difficulty_names[final_word]
arg = ''.join(split_args[:-1])
song: MusicMaster = self.music[arg]
if not song: if not song:
msg = f'Failed to find chart "{arg}".' msg = f'Failed to find chart "{name}".'
await ctx.send(msg) await ctx.send(msg)
self.logger.info(msg) self.logger.info(msg)
return return
@ -153,20 +135,15 @@ class Music(commands.Cog):
await ctx.send(msg) await ctx.send(msg)
self.logger.info(msg) self.logger.info(msg)
return return
self.logger.info(f'Found "{song}" ({romanize(song.name)[1]}).') self.logger.info(f'Found song "{song}" ({romanize(song.name)}).')
embeds, files = self.get_mix_embed_info(song) embeds, files = self.get_mix_embed_info(song)
message = await ctx.send(files=files, embed=embeds[difficulty - 1]) message = await ctx.send(files=files, embed=embeds[difficulty - 1])
reaction_emote_ids = [ reaction_emoji_ids = difficulty_emoji_id.values()
790050636568723466,
790050636489555998,
790050636548276252,
790050636225052694,
]
asyncio.ensure_future(make_tabbed_message(ctx, message, reaction_emote_ids, embeds)) asyncio.ensure_future(make_tabbed_message(ctx, message, reaction_emoji_ids, embeds))
def get_chart_embed_info(self, song): def get_chart_embed_info(self, song):
embeds = [] embeds = []
@ -245,16 +222,16 @@ class Music(commands.Cog):
} }
embed.add_field(name='Info', embed.add_field(name='Info',
value=self.format_info(info), value=format_info(info),
inline=False) inline=False)
embed.add_field(name='Begin', embed.add_field(name='Begin',
value=self.format_info(begin), value=format_info(begin),
inline=True) inline=True)
embed.add_field(name='Middle', embed.add_field(name='Middle',
value=self.format_info(middle), value=format_info(middle),
inline=True) inline=True)
embed.add_field(name='End', embed.add_field(name='End',
value=self.format_info(end), value=format_info(end),
inline=True) inline=True)
embed.set_footer(text='1 column = 10 seconds') embed.set_footer(text='1 column = 10 seconds')
@ -262,6 +239,26 @@ class Music(commands.Cog):
return embeds, files return embeds, files
def parse_chart_args(self, arg: str) -> Tuple[str, ChartDifficulty]:
split_args = arg.split()
difficulty = ChartDifficulty.Expert
if len(split_args) >= 2:
final_word = split_args[-1]
if final_word in self.difficulty_names:
difficulty = self.difficulty_names[final_word]
arg = ''.join(split_args[:-1])
return arg, difficulty
def get_song(self, name_or_id: str) -> Optional[MusicMaster]:
try:
song = asset_manager.music_master[int(name_or_id)]
if song not in self.music.values():
song = self.music[name_or_id]
return song
except (KeyError, ValueError):
return self.music[name_or_id]
def setup(bot): def setup(bot):
bot.add_cog(Music(bot)) bot.add_cog(Music(bot))

@ -0,0 +1,41 @@
from d4dj_utils.master.chart_master import ChartDifficulty
difficulty_emoji_id = {
ChartDifficulty.Easy: 790050636568723466,
ChartDifficulty.Normal: 790050636489555998,
ChartDifficulty.Hard: 790050636548276252,
ChartDifficulty.Expert: 790050636225052694,
}
# \:buff_power: \:buff_heart: \:buff_technique: \:buff_physical:
parameter_bonus_emoji = {
'all': 792095555634331668,
'heart': 792096971040620564,
'technique': 792096971090558986,
'physical': 792096971002216488,
}
parameter_bonus_emoji_by_id = {i: v for i, v in enumerate(parameter_bonus_emoji.values())}
unit_emoji = {
'happy_around': 792069679442821121,
'peaky_pkey': 792076165916524544,
'photon_maiden': 792069679455535136,
'merm4id': 792069679874310184,
'rondo': 792069679770238976,
'lyrical_lily': 792069679673114644,
}
unit_emoji_by_id = {i + 1: v for i, v in enumerate(unit_emoji.values())}
attribute_emoji = {
'street': 791903477986361345,
'party': 791903477999599677,
'cute': 791903477743616003,
'cool': 791903477700755466,
'elegant': 791903477969321985,
}
attribute_emoji_by_id = {i + 1: v for i, v in enumerate(attribute_emoji.values())}
event_point_emoji = 792097816931598336

@ -0,0 +1,2 @@
def format_info(info_entries: dict):
return '\n'.join(f'{k}: {v}' for k, v in info_entries.items() if v)

@ -17,7 +17,7 @@ class FuzzyMap:
self.logger = logging.getLogger(__name__) self.logger = logging.getLogger(__name__)
def values(self): def values(self):
return (v for v in self._values.values() if self.filter(v)) return FuzzyDictValuesView(self)
def __delitem__(self, key): def __delitem__(self, key):
k = romanize(key) k = romanize(key)
@ -42,6 +42,17 @@ class FuzzyMap:
return self._values[result] return self._values[result]
class FuzzyDictValuesView:
def __init__(self, map: FuzzyMap):
self._map = map
def __contains__(self, item):
return item in self._map._values.values() and self._map.filter(item)
def __iter__(self):
yield from (v for v in self._map._values.values() if self._map.filter(v))
@dataclass @dataclass
class FuzzyMatchConfig: class FuzzyMatchConfig:
base_score: float = 0.0 base_score: float = 0.0

@ -1,32 +1,33 @@
import asyncio import asyncio
from typing import List, Callable, Awaitable from typing import List, Callable, Awaitable
from discord import Message, Embed from discord import Message, Embed, Emoji
from discord.ext.commands import Context from discord.ext.commands import Context
async def make_tabbed_message(ctx: Context, message: Message, emote_ids: List[int], embeds: List[Embed], timeout=300): async def make_tabbed_message(ctx: Context, message: Message, emoji_ids: List[int], embeds: List[Embed], timeout=300):
async def callback(index, _ctx, _message): emoji_ids = list(emoji_ids)
await message.edit(embed=embeds[index])
await make_reaction_message(ctx, message, emote_ids, callback, timeout) async def callback(emoji_id, _ctx, _message):
await message.edit(embed=embeds[emoji_ids.index(emoji_id)])
await make_reaction_message(ctx, message, emoji_ids, callback, timeout)
async def make_reaction_message(ctx: Context, message: Message, emote_ids: List[int],
callback: Callable[[int, Context, Message], Awaitable[None]], timeout = 300): async def make_reaction_message(ctx: Context, message: Message, emoji_ids: List[int],
for emote_id in emote_ids: callback: Callable[[int, Context, Message], Awaitable[None]], timeout=300):
await message.add_reaction(ctx.bot.get_emoji(emote_id)) for emoji_id in emoji_ids:
await message.add_reaction(ctx.bot.get_emoji(emoji_id))
def check(rxn, usr): def check(rxn, usr):
return usr == ctx.author and rxn.emoji.id in emote_ids and rxn.message.id == message.id return usr == ctx.author and rxn.emoji.id in emoji_ids and rxn.message.id == message.id
while True: while True:
try: try:
reaction, user = await ctx.bot.wait_for('reaction_add', timeout=timeout, check=check) reaction, user = await ctx.bot.wait_for('reaction_add', timeout=timeout, check=check)
emote_index = emote_ids.index(reaction.emoji.id) await callback(reaction.emoji.id, ctx, message)
await callback(emote_index, ctx, message)
await message.remove_reaction(reaction, user) await message.remove_reaction(reaction, user)
except asyncio.TimeoutError: except asyncio.TimeoutError:
for emote_id in emote_ids: for emoji_id in emoji_ids:
await message.remove_reaction(ctx.bot.get_emoji(emote_id), ctx.bot.user) await message.remove_reaction(ctx.bot.get_emoji(emoji_id), ctx.bot.user)
break break

Loading…
Cancel
Save