Skip to content

Commit 7417547

Browse files
committed
🆕Misc commands
1 parent 282377d commit 7417547

File tree

7 files changed

+163
-52
lines changed

7 files changed

+163
-52
lines changed

.vscode/settings.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
"Dentge",
1111
"dname",
1212
"Donk",
13+
"Donki",
1314
"dotabod",
1415
"erdoc",
1516
"ermtosis",
@@ -19,6 +20,7 @@
1920
"Fossabot",
2021
"GIGA",
2122
"GIGAGUN",
23+
"gunfort",
2224
"heyinoticedyouhaveaprimegamingbadgenexttoyourname",
2325
"ibot",
2426
"kmmrbot",
@@ -32,6 +34,7 @@
3234
"monka",
3335
"moobot",
3436
"Moobot",
37+
"ngrok",
3538
"nightbot",
3639
"nomic",
3740
"oconst",

bot/bot.py

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,17 @@ def print_bot_oauth(self) -> None:
136136

137137
def print_broadcaster_oauth(self) -> None:
138138
"""
139-
https://parrot-thankful-trivially.ngrok-free.app/oauth?scopes=channel:bot&force_verify=true
139+
140+
Notes
141+
-----
142+
* Currently my developer console has localhost as a callback: http://localhost:4343/oauth/callback
143+
But if we ever switch to multi-streams setup then I already have some things set up with
144+
* https://parrot-thankful-trivially.ngrok-free.app/oauth/callback (in developer console)
145+
* ngrok http --url=parrot-thankful-trivially.ngrok-free.app 80 (in my/vps terminal)
146+
Look ngrok dashboard for more.
147+
148+
With it a user needs to go to a link like this:
149+
https://parrot-thankful-trivially.ngrok-free.app/oauth?scopes=channel:bot&force_verify=true
140150
"""
141151
scopes = "%20".join(
142152
[
@@ -145,6 +155,7 @@ def print_broadcaster_oauth(self) -> None:
145155
"channel:moderate",
146156
"channel:read:redemptions",
147157
"channel:manage:broadcast",
158+
"channel:read:subscriptions",
148159
]
149160
)
150161
link = f"http://localhost:4343/oauth?scopes={scopes}&force_verify=true"
@@ -169,9 +180,10 @@ async def setup_hook(self) -> None:
169180

170181
# self.print_bot_oauth()
171182
# self.print_broadcaster_oauth()
183+
# return
172184

173185
for ext in self.extensions:
174-
await self.load_module(ext)
186+
(await self.load_module(ext))
175187

176188
await self.create_eventsub_subscriptions()
177189
self.check_if_online.start()
@@ -216,7 +228,7 @@ async def create_eventsub_subscriptions(self) -> None:
216228
# ✅ Message user:read:chat from the chatting user, channel:bot from broadcaster
217229
sub = eventsub.ChatMessageSubscription(broadcaster_user_id=broadcaster, user_id=bot)
218230
await self.subscribe_websocket(payload=sub)
219-
# Raids to the channel No authorization required
231+
# Raids to the channel No authorization required
220232
sub = eventsub.ChannelRaidSubscription(to_broadcaster_user_id=broadcaster)
221233
await self.subscribe_websocket(payload=sub)
222234
# ✅ Stream went offline No authorization required
@@ -228,6 +240,9 @@ async def create_eventsub_subscriptions(self) -> None:
228240
# ✅ Channel Update (title/game) No authorization required
229241
sub = eventsub.ChannelUpdateSubscription(broadcaster_user_id=broadcaster)
230242
await self.subscribe_websocket(payload=sub)
243+
# ❓ Channel Subscribe (paid) channel:read:subscriptions
244+
sub = eventsub.ChannelSubscribeSubscription(broadcaster_user_id=broadcaster)
245+
await self.subscribe_websocket(payload=sub, token_for=broadcaster, as_bot=False)
231246

232247
@override
233248
async def add_token(self, token: str, refresh: str) -> twitchio.authentication.ValidateTokenPayload:
@@ -244,7 +259,7 @@ async def add_token(self, token: str, refresh: str) -> twitchio.authentication.V
244259
refresh = excluded.refresh;
245260
"""
246261
await self.pool.execute(query, resp.user_id, token, refresh)
247-
log.debug("Added token to the database for user: %s", resp.user_id)
262+
log.info("Added token to the database for user: %s", resp.user_id)
248263
return resp
249264

250265
@override

ext/alerts.py

Lines changed: 23 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -94,43 +94,45 @@ async def raids(self, raid: twitchio.ChannelRaid) -> None:
9494
),
9595
)
9696

97-
@commands.Component.listener(name="stream_offline")
98-
async def stream_end(self, offline: twitchio.StreamOffline) -> None:
99-
"""Stream ended (went offline)."""
100-
await offline.broadcaster.send_message(
101-
sender=self.bot.bot_id,
102-
message=f"Stream is now offline {const.BTTV.Offline}",
103-
)
104-
10597
@commands.Component.listener(name="stream_online")
10698
async def stream_start(self, online: twitchio.StreamOnline) -> None:
10799
"""Stream started (went live)."""
108100
channel_info = await online.broadcaster.fetch_channel_info()
101+
# notification
109102
await online.broadcaster.send_message(
110103
sender=self.bot.bot_id,
111104
message=(
112105
f"Stream just started {const.STV.FeelsBingMan} "
113106
f"Game: {channel_info.game_name} | Title: {channel_info.title}"
114107
),
115108
)
109+
# reminder for the streamer
116110
await online.broadcaster.send_message(
117111
sender=self.bot.bot_id,
118-
message=(f"{online.broadcaster.mention} remember to pin some message, check if everything is working."),
112+
message=f"{online.broadcaster.mention} remember to pin some message, check if everything is working.",
113+
)
114+
115+
@commands.Component.listener(name="stream_offline")
116+
async def stream_end(self, offline: twitchio.StreamOffline) -> None:
117+
"""Stream ended (went offline)."""
118+
await offline.broadcaster.send_message(
119+
sender=self.bot.bot_id,
120+
message=f"Stream is now offline {const.BTTV.Offline}",
119121
)
120122

121123
@commands.Component.listener(name="ad_break")
122124
async def ad_break(self, ad_begin: twitchio.ChannelAdBreakBegin) -> None:
123125
"""Ad break."""
124-
# word = "automatic" if payload.is_automatic else "manual"
126+
word = "automatic" if ad_begin.automatic else "manual"
125127
human_delta = formats.timedelta_to_words(seconds=ad_begin.duration, fmt=formats.TimeDeltaFormat.Short)
126128
await ad_begin.broadcaster.send_message(
127129
sender=self.bot.bot_id,
128-
message=f"{human_delta} ad starting {const.STV.peepoAds}",
130+
message=f"{human_delta} {word} ad starting {const.STV.peepoAds}",
129131
)
130132

131133
# this is pointless probably
132134
# await asyncio.sleep(payload.duration)
133-
# await channel.send("TAd break is over")
135+
# await channel.send("Ad break is over")
134136

135137
@commands.Component.listener(name="ban")
136138
async def bans_timeouts(self, ban: twitchio.Ban) -> None:
@@ -155,6 +157,15 @@ async def bans_timeouts(self, ban: twitchio.Ban) -> None:
155157
# )
156158
# await message.channel.send(content)
157159

160+
@commands.Component.listener(name="subscription")
161+
async def subscription(self, subscribe: twitchio.ChannelSubscribe) -> None:
162+
"""Subscriptions"""
163+
await subscribe.broadcaster.send_message(
164+
sender=self.bot.bot_id,
165+
message=f"{subscribe.user.mention} just subscribed {const.STV.Donki} thanks",
166+
)
167+
168+
158169

159170
async def setup(bot: LueBot) -> None:
160171
"""Load LueBot extension. Framework of twitchio."""

ext/counters.py

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,30 @@ async def component_load(self) -> None:
4242
async def component_teardown(self) -> None:
4343
self.check_first_reward.cancel()
4444

45+
@commands.is_owner()
46+
@commands.group()
47+
async def counter(self, ctx: commands.Context) -> None:
48+
# TODO: I guess, we need to implement "send_help"
49+
await ctx.send("Use this command together with subcommands delete/create/change")
50+
51+
@counter.command()
52+
async def create(self, ctx: commands.Context, name: str) -> None:
53+
query = """
54+
INSERT INTO ttv_counters
55+
(name, value)
56+
VALUES ($1, $2)
57+
ON CONFLICT (name)
58+
DO NOTHING
59+
RETURNING value;
60+
"""
61+
value: int | None = await self.bot.pool.fetchval(query, name, 0)
62+
if value is None:
63+
await ctx.send(f"Such counter already exists! {const.STV.POLICE}")
64+
else:
65+
await ctx.send(f"Created a counter `{name}` (current value = {value}) {const.STV.science}")
66+
67+
# TODO: counter set (change)/increment (add) / remove commands
68+
4569
# ERM COUNTERS
4670

4771
@commands.Component.listener(name="message")
@@ -152,7 +176,7 @@ async def test_digits(self, ctx: commands.Context) -> None:
152176
content = " ".join(const.DIGITS)
153177
await ctx.send(content)
154178

155-
@lueloop(count=1) # time=[datetime.time(hour=4, minute=59)])
179+
@lueloop(time=[datetime.time(hour=4, minute=59)])
156180
async def check_first_reward(self) -> None:
157181
"""The task that ensures the reward "First" under a specific id exists.
158182

ext/misc_commands.py

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
from __future__ import annotations
2+
3+
from typing import TYPE_CHECKING
4+
5+
from twitchio.ext import commands
6+
7+
from bot import LueComponent
8+
from utils import const
9+
10+
if TYPE_CHECKING:
11+
from bot import LueBot
12+
13+
14+
class MiscellaneousCommands(LueComponent):
15+
"""Miscellaneous commands.
16+
17+
Commands that are likely to be removed in future.
18+
"""
19+
20+
@commands.command()
21+
async def erdoc(self, ctx: commands.Context) -> None:
22+
"""Link to my Elden Ring notes."""
23+
await ctx.send( # cspell: disable-next-line
24+
"My notes with everything Elden Ring related: "
25+
"docs.google.com/document/d/19vTJVS7k1zdmShOAcO41KBWepfybMsTprQ208O7HpLU"
26+
)
27+
28+
@commands.command()
29+
async def run(self, ctx: commands.Context) -> None:
30+
"""Explanation of my first Sekiro hitless run."""
31+
msg = (
32+
"All Memories & Unique MiniBosses (so no repetitions). "
33+
"The idea: I have to learn and practice all boss move-sets. "
34+
"Even load Inner Father, Fire Isshin and Emma save-file. "
35+
"Almost Charmless (take it back pre-demon). Mostly Sword+Shuriken. Somewhat loop-less. "
36+
"For more look !notes."
37+
)
38+
await ctx.send(msg)
39+
40+
@commands.command(aliases=["sekironotes", "notes", "skdoc"])
41+
async def sekirodoc(self, ctx: commands.Context) -> None:
42+
"""Link to my Sekiro notes."""
43+
await ctx.send( # cspell: disable-next-line
44+
"My notes with everything Sekiro related: "
45+
"docs.google.com/document/d/1rjp7lhvP0vwwlO7bC7TyFAjKcGDovFuo2EYUaX66QiA"
46+
)
47+
48+
@commands.group(invoke_fallback=True)
49+
async def gunfort(self, ctx: commands.Context) -> None:
50+
"""Commands to count my successful and failed Yolo Gunfort attempts."""
51+
query = "SELECT value FROM ttv_counters WHERE name = $1;"
52+
success: int = await self.bot.pool.fetchval(query, "gunfort_success")
53+
query = "SELECT value FROM ttv_counters WHERE name = $1;"
54+
attempts: int = await self.bot.pool.fetchval(query, "gunfort_attempts")
55+
56+
stats = f" Success Rate: {success/attempts:.1%} (over {attempts} attempts)"
57+
await ctx.send(f"{const.STV.science} Yolo Gunfort {stats}")
58+
59+
@commands.is_owner()
60+
@gunfort.command()
61+
async def no(self, ctx: commands.Context) -> None:
62+
"""Count a failed Yolo Gunfort attempt in."""
63+
query = "SELECT value FROM ttv_counters WHERE name = $1;"
64+
success: int = await self.bot.pool.fetchval(query, "gunfort_success")
65+
query = "UPDATE ttv_counters SET value = value + 1 WHERE name = $1 RETURNING value;"
66+
attempts: int = await self.bot.pool.fetchval(query, "gunfort_attempts")
67+
68+
stats = f" Success Rate: {success/attempts:.1%} (over {attempts} attempts)"
69+
await ctx.send(f"Failed yolo gunfort? {const.STV.classic} {stats}")
70+
71+
@commands.is_owner()
72+
@gunfort.command()
73+
async def yes(self, ctx: commands.Context) -> None:
74+
"""Count a successful Yolo Gunfort attempt in."""
75+
query = "UPDATE ttv_counters SET value = value + 1 WHERE name = $1 RETURNING value;"
76+
success: int = await self.bot.pool.fetchval(query, "gunfort_success")
77+
query = "UPDATE ttv_counters SET value = value + 1 WHERE name = $1 RETURNING value;"
78+
attempts: int = await self.bot.pool.fetchval(query, "gunfort_attempts")
79+
80+
stats = f" Success Rate: {success/attempts:.1%} (over {attempts} attempts)"
81+
await ctx.send(f"Yolo Gunfort is easy {const.STV.EZdodge} {stats}")
82+
83+
84+
async def setup(bot: LueBot) -> None:
85+
"""Load LueBot extension. Framework of twitchio."""
86+
await bot.add_component(MiscellaneousCommands(bot))

ext/simple_commands.py

Lines changed: 2 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
from bot import LueBot
1818

1919

20-
class DefaultCommands(LueComponent):
20+
class SimpleCommands(LueComponent):
2121
"""Simple commands.
2222
2323
Simple in a sense that they are just somewhat static and their implementation is simple.
@@ -26,39 +26,6 @@ class DefaultCommands(LueComponent):
2626
sitting in the database like commands from `custom_commands.py` do.
2727
"""
2828

29-
# 1. TEMPORARY COMMANDS
30-
31-
@commands.command()
32-
async def erdoc(self, ctx: commands.Context) -> None:
33-
"""Link to my Elden Ring notes."""
34-
await ctx.send( # cspell: disable-next-line
35-
"My notes with everything Elden Ring related: "
36-
"docs.google.com/document/d/19vTJVS7k1zdmShOAcO41KBWepfybMsTprQ208O7HpLU"
37-
)
38-
39-
@commands.command()
40-
async def run(self, ctx: commands.Context) -> None:
41-
"""Explanation of my first Sekiro hitless run."""
42-
msg = (
43-
"All Memories & Unique MiniBosses (so no repetitions). "
44-
"The idea: I have to learn and practice all boss move-sets. "
45-
"Even load Inner Father, Fire Isshin and Emma save-file. "
46-
"Almost Charmless (take it back pre-demon). Mostly Sword+Shuriken. Somewhat loop-less. "
47-
"For more look !notes."
48-
)
49-
await ctx.send(msg)
50-
51-
@commands.command(aliases=["sekironotes", "notes", "skdoc"])
52-
async def sekirodoc(self, ctx: commands.Context) -> None:
53-
"""Link to my Sekiro notes."""
54-
await ctx.send( # cspell: disable-next-line
55-
"My notes with everything Sekiro related: "
56-
"docs.google.com/document/d/1rjp7lhvP0vwwlO7bC7TyFAjKcGDovFuo2EYUaX66QiA"
57-
)
58-
59-
# 2. MORE OR LESS STABLE COMMANDS
60-
# (sorted alphabetically)
61-
6229
@guards.is_online()
6330
@commands.command()
6431
async def clip(self, ctx: commands.Context) -> None:
@@ -356,4 +323,4 @@ async def twitch_id(self, ctx: commands.Context, *, user: twitchio.User) -> None
356323

357324
async def setup(bot: LueBot) -> None:
358325
"""Load LueBot extension. Framework of twitchio."""
359-
await bot.add_component(DefaultCommands(bot))
326+
await bot.add_component(SimpleCommands(bot))

utils/const.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ class STV(StrEnum):
7171
ApuBritish = "ApuBritish"
7272
AYAYA = "AYAYA"
7373
Cinema = "Cinema"
74+
classic = "classic"
7475
DankApprove = "DankApprove"
7576
dankFix = "dankFix"
7677
dankHey = "dankHey"
@@ -88,9 +89,11 @@ class STV(StrEnum):
8889
donkHey = "donkHey"
8990
donkJam = "donkJam"
9091
donkSad = "donkSad"
92+
Donki = "Donki"
9193
DonkPrime = "DonkPrime"
9294
ermtosis = "ermtosis"
9395
Erm = "Erm"
96+
EZdodge = "EZdodge"
9497
FeelsBingMan = "FeelsBingMan"
9598
FirstTimeChadder = "FirstTimeChadder"
9699
FirstTimeDentge = "FirstTimeDentge"
@@ -106,6 +109,8 @@ class STV(StrEnum):
106109
plink = "plink"
107110
PogChampPepe = "PogChampPepe"
108111
POGCRAZY = "POGCRAZY"
112+
POLICE = "POLICE"
113+
science = "science"
109114
yo = "yo"
110115
uuh = "uuh"
111116
wickedchad = "wickedchad"

0 commit comments

Comments
 (0)