Skip to content

Commit 7fae773

Browse files
committed
Bug Command Tests, small cleanup, discord webhook for errors.
1 parent 999583b commit 7fae773

File tree

11 files changed

+150
-73
lines changed

11 files changed

+150
-73
lines changed

bot/bot.py

Lines changed: 2 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
11
# -*- coding: utf-8 -*-
22
from __future__ import annotations
33

4-
import asyncio
54
import datetime
6-
from asyncio import Task
75
from collections import defaultdict
86
from logging import Logger
97
from typing import TYPE_CHECKING, Callable
@@ -24,12 +22,11 @@
2422
LifecycleHandler,
2523
TokensHandler,
2624
)
27-
from bot.utils import Cache, Config, MarkovProcessor, MemCache, TimeTools
25+
from bot.utils import Cache, Config, MemCache, TimeTools
2826

2927
if TYPE_CHECKING:
3028
# from bot.api import api, api_start
31-
from bot.ext import Context, Routine
32-
from bot.models import Channel as ChannelModel
29+
from bot.ext import Context
3330

3431

3532
class Gorenmu(TypesBot):
@@ -43,37 +40,20 @@ def __init__(self, configs: Config, case_insensitive: bool, log: Logger, adapter
4340
adapter=adapter,
4441
)
4542
self.boot: datetime.datetime = datetime.datetime.now(datetime.timezone.utc)
46-
self.lottery_lock: asyncio.Lock = asyncio.Lock()
47-
self.channels: dict[str, ChannelModel] = {}
48-
self.routines: list[Routine] = []
49-
self.bots_ids: list[int] = []
5043
self.manual_events: defaultdict[str, dict[str, dict[str, Callable]]] = defaultdict(lambda: defaultdict(dict))
5144
# self.api: api | None = None
5245
# self.api_start: api_start = None
53-
self.MarkovProcessor: MarkovProcessor | None = None
54-
self.MarkovTask: Task[None] | None = None
55-
self.dev_name: str | None = None
56-
self.bot_nick: str | None = None
5746
self.log: Logger = log
5847
self.config: Config = configs
5948
self.memcache: MemCache = MemCache()
60-
# self.StringTools: StringTools = StringTools()
6149
self.cache: RedisCache | MemcachedCache | SimpleMemoryCache = Cache.cache_load(bot=self)
62-
# self.docs_handler: DynamicDescriptions = DynamicDescriptions(self)
63-
# self.UploadThings: UploadThings = UploadThings(self)
64-
self.docs: defaultdict = defaultdict(dict)
65-
66-
# self.SessionsCaches: SessionsCaches = SessionsCaches(self)
67-
# self.TranslationManager: TranslationManager = TranslationManager()
68-
# self.Emotes: Emotes = Emotes(bot=self)
6950
self.TokensHandler: TokensHandler = TokensHandler(bot=self)
7051
self.DatabaseHandler: DatabaseHandler = DatabaseHandler(bot=self)
7152
self.ChannelHandler: ChannelHandler = ChannelHandler(bot=self)
7253
self.CommandHandler: CommandHandler = CommandHandler(bot=self)
7354
self.LifecycleHandler: LifecycleHandler = LifecycleHandler(bot=self)
7455
self.ContextHandler: ContextHandler = ContextHandler(bot=self)
7556
self.TimeTools: TimeTools = TimeTools()
76-
self.mock: bool = False
7757

7858
async def add_token(self, token: str, refresh: str) -> twitchio.authentication.ValidateTokenPayload:
7959
return await self.TokensHandler.add_token(token, refresh)

bot/cogs/admin/commands/admin.py

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -92,18 +92,6 @@ async def reload(self, ctx: Context, command: str, *, extras=False) -> Response:
9292
"class": "CommandHandler",
9393
"args": [self.bot],
9494
},
95-
"session_caches": {
96-
"attr": "SessionsCaches",
97-
"module": "bot.utils.cache_sessions",
98-
"class": "SessionsCaches",
99-
"args": [self.bot],
100-
},
101-
"string_mani": {
102-
"attr": "StringTools",
103-
"module": "bot.utils.string_manipulation",
104-
"class": "StringTools",
105-
"args": [],
106-
},
10795
}
10896

10997
if command == "commands":

bot/cogs/admin/commands/translations.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,8 @@ def deco_commands(self, ctx: Context, *args, **kwargs) -> CommandExemples: # NO
5353
def deco_admonitions(self, ctx: Context, *args, **kwargs) -> Admonitions: # NOQA
5454
return Admonitions([])
5555

56+
Nada: Nada
57+
5658
class Restart(TBase):
5759
def __init__(self):
5860
super().__init__()
@@ -96,6 +98,8 @@ def deco_admonitions(self, ctx: Context, *args, **kwargs) -> Admonitions:
9698
self.lang_dict.add_with(["pt_br", "pt"], Admonitions([]))
9799
return self._untangle_admonitions(ctx, "admonitions")
98100

101+
Restart: Restart
102+
99103
class Reload(TBase):
100104
def __init__(self):
101105
super().__init__()
@@ -220,3 +224,5 @@ def deco_admonitions(self, ctx: Context, *args, **kwargs) -> Admonitions:
220224
self.lang_dict.add_with("en", Admonitions([]))
221225
self.lang_dict.add_with(["pt_br", "pt"], Admonitions([]))
222226
return self._untangle_admonitions(ctx, "admonitions")
227+
228+
Reload: Reload

bot/ext/bot.py

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,31 @@
11
# -*- coding: utf-8 -*-
22
from __future__ import annotations
33

4+
import asyncio
5+
from asyncio import Task
46
from typing import TYPE_CHECKING, Optional
57

68
from twitchio.ext import commands
79

810
if TYPE_CHECKING:
9-
from bot.ext import commands
11+
from bot.ext import Routine, commands
1012
from bot.ext.commands import Command, Group
13+
from bot.models import Channel as ChannelModel
14+
from bot.utils import MarkovProcessor
1115

1216

1317
class TypesBot(commands.AutoBot):
18+
def __init__(self, *args, **kwargs):
19+
super().__init__(*args, **kwargs)
20+
self.MarkovProcessor: MarkovProcessor | None = None
21+
self.MarkovTask: Task[None] | None = None
22+
self.dev_name: str | None = None
23+
self.bot_nick: str | None = None
24+
self.mock: bool = False
25+
self.lottery_lock: asyncio.Lock = asyncio.Lock()
26+
self.channels: dict[str, ChannelModel] = {}
27+
self.routines: list[Routine] = []
28+
self.bots_ids: list[int] = []
29+
1430
def get_command(self, name: str, /) -> Optional[Command | Group]:
1531
return super().get_command(name)

bot/ext/commands.py

Lines changed: 5 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -98,33 +98,20 @@ def __new__(cls, *args, **kwargs) -> Self:
9898
rate = getattr(self, "cooldown_rate", 10)
9999
per = getattr(self, "cooldown_per", 3)
100100
key = getattr(self, "cooldown_key", BucketType.user)
101-
bucket_: Bucket[Context] = Bucket.from_cooldown(base=Cooldown, key=key, **{"per": per, "rate": rate})
101+
bucket_: Bucket[Context] = Bucket.from_cooldown(base=Cooldown, key=key, **{"per": per, "rate": rate}) # NOQA
102102
category_name: str = getattr(self, "name", self.__class__.__name__)
103103
category_name = category_name.removesuffix("Cmd").removesuffix("Cmds")
104104
self.inject_events(bot, cls, self, category_name)
105105
for command_name in self.__all_commands__:
106106
command_: Command = self.__all_commands__[command_name]
107107
if hasattr(command_, "commands"):
108108
for name in command_.commands:
109-
self._extras(command_.commands[name], bucket_, bot)
110-
bot.docs[category_name][command_.commands[name].name] = command_.commands[name].docs
111-
self._extras(command_, bucket_, bot)
112-
bot.docs[category_name][command_.name] = command_.docs
109+
if len(command_.commands[name]._buckets) == 0: # NOQA
110+
command_.commands[name]._buckets.append(bucket_) # NOQA
111+
if len(command_._buckets) == 0: # NOQA
112+
command_._buckets.append(bucket_) # NOQA
113113
return self
114114

115-
@staticmethod
116-
def _extras(new_command: Command, bucket_, bot: Gorenmu):
117-
if len(new_command._buckets) == 0: # NOQA
118-
new_command._buckets.append(bucket_) # NOQA
119-
120-
def docs():
121-
if new_command.template:
122-
return bot.docs_handler.template_description(new_command)
123-
else:
124-
return bot.docs_handler.normal_description(new_command)
125-
126-
new_command.docs = docs
127-
128115
@staticmethod
129116
def inject_events(bot, cls, self, category_name):
130117
if category_name in bot.manual_events:
@@ -137,15 +124,6 @@ def inject_events(bot, cls, self, category_name):
137124
bot.manual_events[category_name][event_name][name] = injected
138125

139126

140-
def base_decorator(base: str, template=False) -> Callable[[Command], Command]:
141-
def decorator(command: Command) -> Command: # NOQA
142-
command.decorator_path = base
143-
command.template = template
144-
return command
145-
146-
return decorator
147-
148-
149127
def command(
150128
name: str | None = None,
151129
aliases: list[str] | None = None,
@@ -239,12 +217,3 @@ def decorator(func: Callable):
239217
return func
240218

241219
return decorator
242-
243-
244-
# def event_handler(event_name: str):
245-
# def decorator(func: Callable):
246-
# async def wrapper(self, *args, **kwargs):
247-
# return await func(self, *args, **kwargs)
248-
# wrapper._event_info = event_name
249-
# return wrapper
250-
# return decorator

bot/handlers/command_handler.py

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,10 @@
33

44
import os
55
import pathlib
6+
import traceback
67
import types
78
from importlib import import_module
8-
from typing import TYPE_CHECKING, Any
9+
from typing import TYPE_CHECKING
910

1011
from loguru import logger
1112
from twitchio.ext.commands import CommandErrorPayload
@@ -15,6 +16,7 @@
1516

1617
if TYPE_CHECKING:
1718
from bot.bot import Gorenmu
19+
from bot.cogs.bug.command.bug import BugCmd
1820
from bot.ext import Context
1921

2022

@@ -103,6 +105,29 @@ def _get_module(path: pathlib.Path, filename: pathlib.Path) -> tuple[types.Modul
103105
module: types.ModuleType = import_module(name, package=package)
104106
return module, name
105107

108+
async def send_bug(self, ctx: Context, error: Exception):
109+
command = self.bot.get_command("bug")
110+
url = self.bot.config.Discord.log_webhook
111+
component: BugCmd = command.component # NOQA
112+
session = component.SessionsCaches.Bug.session
113+
discord_webhook = component.DiscordWebHook
114+
tb_str = "".join(traceback.format_exception(type(error), error, error.__traceback__))
115+
user_url = f"[@{ctx.author.name}](<https://twitch.tv/{ctx.author.name}>)"
116+
channel_url = f"[#{ctx.channel.name}](<https://twitch.tv/{ctx.channel.name}>)"
117+
await discord_webhook.send_discord_webhook(
118+
session,
119+
url,
120+
ctx,
121+
f"**Args used in the command:** **```{ctx.message.text}```**\n```py\n{tb_str}\n```",
122+
f"Error occurred while user {user_url} "
123+
f"was running the command: {ctx.command.name} "
124+
f"in {channel_url} channel.",
125+
f"https://twitch.tv/{ctx.author.name}",
126+
title2=f"Error occurred while user @{ctx.author.name} "
127+
f"was running the command: {ctx.command.name} "
128+
f"in #{ctx.channel.name} channel.",
129+
)
130+
106131
async def event_command_error(self, payload: CommandErrorPayload) -> None:
107132
command = payload.context.command
108133
if command and command.has_error and payload.context.error_dispatched:
@@ -115,6 +140,10 @@ async def event_command_error(self, payload: CommandErrorPayload) -> None:
115140
if ctx.prefix != self.bot.channels[ctx.channel.name].prefix:
116141
return None
117142
translations = ctx.command.component.translations
143+
144+
if not isinstance(error, InvalidArgument):
145+
await self.send_bug(ctx, error)
146+
118147
if isinstance(error, CommandNotFound):
119148
return None
120149
if isinstance(error, DevRequired):

bot/utils/discord_webhook.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ async def send_discord_webhook(
2323
title: str,
2424
author_url: str,
2525
embeds: list[DiscordEmbed] = None,
26+
title2: str = None,
2627
) -> aiohttp.ClientResponse:
2728
to_mark = ctx.bot.config.Discord.to_mark
2829
avatar_webhook = ctx.bot.config.Discord.webhook_avatar
@@ -40,7 +41,7 @@ async def send_discord_webhook(
4041
)
4142
embed = DiscordEmbed()
4243
embed.set_author(name=display_name, url=author_url, icon_url=profile_image)
43-
embed.set_title(title)
44+
embed.set_title(title2 or title)
4445
embed.set_description(f"{content}\n\n{log_url1}\n{log_url2}\n{log_url3}")
4546
embed.set_timestamp(ctx.message.timestamp)
4647
embed.set_color(ctx.author.color.hex or "03b2f8")

tests/tests/cogs/bug/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
# -*- coding: utf-8 -*-
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
# -*- coding: utf-8 -*-
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
# -*- coding: utf-8 -*-
2+
3+
import pytest
4+
import pytest_asyncio
5+
6+
from bot.cogs.bug.command.bug import BugCmd
7+
from tests.helpers.mock_classes import MockContext
8+
9+
from .test_params import Params
10+
11+
12+
@pytest_asyncio.fixture
13+
async def interact(mock_bot):
14+
return BugCmd(bot=mock_bot)
15+
16+
17+
@pytest.mark.asyncio
18+
@pytest.mark.parametrize("lang, helper, usage", Params.decorators)
19+
async def test_decorators(interact, mock_context: MockContext, lang: str, helper: str, usage: str):
20+
await mock_context.prepare_context(lang)
21+
mock_context.Asserter.assert_string(interact.translations.Bug.deco_usage(mock_context, "+"), usage, strict=True)
22+
mock_context.Asserter.assert_string(interact.translations.Bug.deco_helper(mock_context, "+"), helper, strict=True)
23+
24+
25+
# To lazy to make this tests too.
26+
27+
# async def base_bug(
28+
# interact, mock_context: MockContext, lang: str, content: str, expected: str = None, re_expected: str = None, success: bool = False
29+
# ):
30+
# await mock_context.prepare_context(lang)
31+
# response: Response = await interact.bug._callback(interact, mock_context, content=content) # NOQA
32+
# mock_context.Asserter.assert_string(response.response_string, expected=expected, re_expected=re_expected)
33+
# mock_context.Asserter.assert_boolean(response.success, success)
34+
#
35+
#
36+
# @pytest.mark.asyncio
37+
# @pytest.mark.parametrize("lang, expected", Params.no_content)
38+
# async def test_no_content(interact, mock_context: MockContext, lang: str, expected: str):
39+
# await base_bug(
40+
# interact,
41+
# mock_context,
42+
# lang=lang,
43+
# content="",
44+
# expected=expected,
45+
# success=True
46+
# )
47+
#
48+
#
49+
# @pytest.mark.asyncio
50+
# @pytest.mark.parametrize("lang, expected", Params.no_content)
51+
# async def test_(interact, mock_context: MockContext, lang: str, expected: str):
52+
# await base_bug(
53+
# interact,
54+
# mock_context,
55+
# lang=lang,
56+
# content="",
57+
# expected=expected,
58+
# success=True
59+
# )

0 commit comments

Comments
 (0)