Skip to content

Commit 62fdb45

Browse files
authored
Add files via upload
1 parent 15780bf commit 62fdb45

40 files changed

+3357
-0
lines changed

config.json

+69
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
{
2+
"http_config": {
3+
"redirect_uri": "http://localhost:5050/callback",
4+
"scopes": [
5+
"channel:moderate",
6+
"channel:read:subscriptions",
7+
"channel:read:hype_train",
8+
"channel:read:goals",
9+
"channel:read:charity",
10+
"channel:manage:polls",
11+
"channel:manage:raids",
12+
"channel:manage:predictions",
13+
"channel:manage:broadcast",
14+
"channel:manage:redemptions",
15+
"channel:manage:moderators",
16+
"channel:edit:commercial",
17+
"user:read:emotes",
18+
"user:read:chat",
19+
"user:write:chat",
20+
"bits:read",
21+
"clips:edit",
22+
"moderator:manage:banned_users",
23+
"moderator:manage:shoutouts",
24+
"moderator:read:followers"
25+
]
26+
},
27+
"ws_config": {
28+
"storage_type": "json",
29+
"channels": {
30+
"channel.channel_points_custom_reward_redemption.add": null,
31+
"channel.ban": null,
32+
"channel.follow": null,
33+
"channel.cheer": null,
34+
"channel.raid": null,
35+
"channel.chat.notification": null,
36+
"channel.chat.message": null,
37+
"channel.subscribe": null,
38+
"channel.subscription.gift": null,
39+
"channel.subscription.message": null,
40+
"channel.goal.progress": null,
41+
"channel.hype_train.progress": null,
42+
"channel.hype_train.end": null
43+
},
44+
"queue_skip": [
45+
"channel.chat.message",
46+
"channel.goal.progress",
47+
"channel.hype_train.progress",
48+
"channel.hype_train.end",
49+
"channel.chat.notification"
50+
]
51+
},
52+
"ai_cfg": {
53+
"persona": "GenZbot",
54+
"model": "Llama323BF16",
55+
"device": "cuda"
56+
},
57+
"obs_cfg":{
58+
"ignore_media": ["RustFlashbang", "Sun Spin", "flies", "fire1", "fire2", "fire3", "fire4"],
59+
"media_scenes": ["[S] Videos", "[S] TWSS", "[S] Bit Alerts"]
60+
},
61+
"goals_cfg": {
62+
"name": "mygoal",
63+
"total": 84000
64+
},
65+
"login_browser": {"firefox": "C:\\Program Files\\Mozilla Firefox\\firefox.exe"},
66+
"max_retries": 60,
67+
"retry_delay": 5,
68+
"static_dirs": ["static"]
69+
}

customalerts/__init__.py

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
from .bans import ChannelBanAlert
2+
from .goals import ChannelGoalProgressAlert
3+
from .cheerfollowraid import ChannelCheerAlert, ChannelRaidAlert, ChannelFollowAlert
4+
from .chat import ChannelChatMessageAlert, ChannelChatNotificationAlert
5+
from .channelpoints import ChannelChannelPointsCustomRewardRedemptionAddAlert
6+
from .hypetrain import ChannelHypeTrainProgressAlert, ChannelHypeTrainEndAlert
7+
from .subscriptions import ChannelSubscribeAlert, ChannelSubscriptionGiftAlert, ChannelSubscriptionMessageAlert
8+
9+
alert_objs = {
10+
'channel.chat.notification': ChannelChatNotificationAlert,
11+
'channel.chat.message': ChannelChatMessageAlert,
12+
'channel.ban': ChannelBanAlert,
13+
'channel.channel_points_custom_reward_redemption.add': ChannelChannelPointsCustomRewardRedemptionAddAlert,
14+
'channel.hype_train.progress': ChannelHypeTrainProgressAlert,
15+
'channel.hype_train.end': ChannelHypeTrainEndAlert,
16+
'channel.subscribe': ChannelSubscribeAlert,
17+
'channel.subscription.message': ChannelSubscriptionMessageAlert,
18+
'channel.subscription.gift': ChannelSubscriptionGiftAlert,
19+
'channel.goal.progress': ChannelGoalProgressAlert,
20+
'channel.cheer': ChannelCheerAlert,
21+
'channel.follow': ChannelFollowAlert,
22+
'channel.raid': ChannelRaidAlert
23+
}

customalerts/bans.py

+66
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
from poolguy.utils import ColorLogger, json, random, asyncio
2+
from poolguy.twitchws import Alert
3+
import aiofiles
4+
5+
logger = ColorLogger("BanAlert")
6+
7+
class ChannelBanAlert(Alert):
8+
"""channel.ban"""
9+
def __init__(self, *args, **kwargs):
10+
super().__init__(*args, **kwargs)
11+
self.banPages = ['templates/ban-pacific.html', 'templates/ban-tucker.html']
12+
self.ignorelist = ["streamelements", "nightbot"]
13+
self.defaultpic = "default_pic.jpg"
14+
self.scene = "[M] Main"
15+
self.source = "[S] Banned"
16+
self.ai_thread = None
17+
18+
async def wait_till_done(self):
19+
logger.debug(f"[Alert] Ban: waiting for AI")
20+
while self.ai_thread:
21+
if not self.ai_thread.is_alive():
22+
r = self.ai_thread.join()
23+
await self.bot.http.sendChatMessage(f'{r}')
24+
self.ai_thread = None
25+
await asyncio.sleep(0.1)
26+
logger.debug(f"[Alert] Ban: waiting for alert")
27+
while not self.bot.alertDone:
28+
await asyncio.sleep(0.1)
29+
30+
async def getUserPic(self, uid):
31+
logger.debug(f"[Alert] Ban: getting user pic")
32+
try:
33+
r = await self.bot.http.getUsers(ids=uid)
34+
return r[0]['profile_image_url']
35+
except Exception as e:
36+
logger.error(f"Error getting user info: {e}")
37+
return self.defaultpic
38+
39+
async def render_page(self, page, name, pic):
40+
try:
41+
async with aiofiles.open(page, 'r', encoding='utf-8') as f:
42+
template = await f.read()
43+
rendered = template.replace("{{ name }}", name).replace("{{ pic_url }}", pic)
44+
logger.debug(f"[Alert] Ban: rendered template for {name}")
45+
return rendered
46+
except Exception as e:
47+
logger.error(f"[Alert] Ban: Error rendering template: {e}")
48+
return ""
49+
50+
async def process(self):
51+
self.bot.alertDone = False
52+
logger.debug(f"[Alert] Ban: \n{json.dumps(self.data, indent=2)}")
53+
if self.data["moderator_user_login"] in self.ignorelist:
54+
return
55+
dur = "permanently banned" if self.data['is_permanent'] else "timed out"
56+
reason = self.data['reason']
57+
name = self.data['user_name']
58+
logger.debug(f"[Alert] Ban: starting ai thread")
59+
q = f'Please inform everyone that "{name}" was just {dur} from the chat for the reason: "{reason if reason else "Acting like a bot"}".'
60+
self.ai_thread = self.bot.ai.threaded_ask(q)
61+
picurl = await self.getUserPic(self.data["user_id"])
62+
banPage = random.choice(self.banPages)
63+
self.bot.banHTML = await self.render_page(banPage, name, picurl)
64+
await self.bot.obsws.show_source(self.source, self.scene)
65+
await self.wait_till_done()
66+
await self.bot.obsws.hide_source(self.source, self.scene)

customalerts/channelpoints.py

+120
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
import keyboard
2+
from pydub import AudioSegment, playback
3+
from poolguy.utils import asyncio, json, time, random, threading
4+
from poolguy.utils import ColorLogger, loadJSON
5+
from poolguy.twitchws import Alert
6+
7+
logger = ColorLogger("ChannelPointsAlert")
8+
9+
cfg = loadJSON("db/chan_points_cfg.json")
10+
11+
class ChannelChannelPointsCustomRewardRedemptionAddAlert(Alert):
12+
"""channel.channel_points_custom_reward_redemption.add"""
13+
def __init__(self, *args, **kwargs):
14+
super().__init__(*args, **kwargs)
15+
self.avatar_scene = "[S] Avatar"
16+
self.vidscene = "[S] Videos"
17+
self.clown_sources = [
18+
"clown-foldpizza",
19+
"clown-pain",
20+
"clown-3g",
21+
"clown-tech",
22+
"clown-drivethru",
23+
"clown-sub",
24+
"clown-hell"
25+
]
26+
self.cabbage_emotes = [
27+
's4w3d0FfPurp',
28+
's4w3d0FfGoldCabbage',
29+
's4w3d0FfCabbage',
30+
's4w3d0FfCabbageJAM'
31+
]
32+
33+
async def timeout(self, size=60):
34+
viewer2ban = self.data["user_input"].replace("@", "").replace(" ", "")
35+
logger.warning(f"Banning {viewer2ban}")
36+
r = await self.bot.http.getUsers(logins=viewer2ban)
37+
try:
38+
banId = r[0]['id']
39+
await self.bot.http.banUser(
40+
user_id=banId,
41+
duration=size,
42+
reason=f"{self.data['user_login']} used channel points to timeout {viewer2ban} for {size} secs"
43+
)
44+
except Exception as e:
45+
logger.error("[Bot] "+ e)
46+
47+
def addSoundOnKeyTask(self, sound, duration, gain):
48+
key = self.data["user_input"].replace(" ", "").lower()
49+
asyncio.create_task(self.soundOnKey(key=key, sound=sound, duration=duration, gain=gain))
50+
51+
def play_sound(self, sound, gain=0):
52+
logger.debug(f"[play_sound] -> {sound}")
53+
seg = AudioSegment.from_file(sound)
54+
s = seg.apply_gain(gain)
55+
threading.Thread(target=playback.play, args=(s,)).start()
56+
57+
async def soundOnKey(self, key="space", sound="fart1.wav", duration=30, gain=0):
58+
start = time.time()
59+
logger.debug(f"[soundOnKey] -> ({key}):{sound} -{duration}sec-")
60+
played = False
61+
while time.time()-start < duration:
62+
if keyboard.is_pressed(key):
63+
if not played:
64+
played = True
65+
self.play_sound(sound, gain)
66+
else:
67+
played = False
68+
await asyncio.sleep(0.05)
69+
logger.debug(f"[soundOnKey] -> ({key}):{sound} -complete-")
70+
71+
async def process(self):
72+
rewardId = self.data["reward"]["id"]
73+
if rewardId not in cfg:
74+
logger.error(f"ChannelPointRedeem: {rewardId} \n{json.dumps(self.data, indent=2)}")
75+
return
76+
rewardConf = cfg[rewardId]
77+
if "source" in rewardConf: # Source to hide?
78+
if "delay" in rewardConf:
79+
await self.bot.obsws.show_source(rewardConf['source'], rewardConf['scene'])
80+
await asyncio.sleep(rewardConf['delay'])
81+
await self.bot.obsws.hide_source(rewardConf['source'], rewardConf['scene'])
82+
else:
83+
await self.bot.obsws.show_and_wait(rewardConf['source'], rewardConf['scene'])
84+
#===============================================================#
85+
if 'name' in rewardConf:
86+
match rewardConf['name']:
87+
# Ban_______________
88+
case "bansmall":
89+
await self.timeout(60)
90+
case "banbig":
91+
await self.timeout(3600)
92+
# Cabbage____________
93+
case "cabbage":
94+
for i in range(3):
95+
await asyncio.sleep(0.3)
96+
await self.bot.http.sendChatMessage(f'{random.choice(self.cabbage_emotes)} {random.choice(self.cabbage_emotes)} {random.choice(self.cabbage_emotes)}')
97+
# Sound on Key____________
98+
case "mario.mp3":
99+
self.addSoundOnKeyTask("db/sounds/mario-jump.mp3", 300, -15)
100+
case "ctrl_fart":
101+
self.addSoundOnKeyTask("db/sounds/farts/fart1.wav", 300, -15)
102+
# Avatars____________
103+
case "monke":
104+
await self.bot.obsws.hide_all_sources(self.avatar_scene)
105+
await self.bot.obsws.show_source("[Av] Monke", self.avatar_scene)
106+
case "scav":
107+
await self.bot.obsws.hide_all_sources(self.avatar_scene)
108+
await self.bot.obsws.show_source("[Av] Scav", self.avatar_scene)
109+
case "kek":
110+
await self.bot.obsws.hide_all_sources(self.avatar_scene)
111+
await self.bot.obsws.show_source("[Av] Kek", self.avatar_scene)
112+
# Clowncore____________
113+
case "clowncore":
114+
await self.bot.obsws.show_and_wait(random.choice(self.clown_sources), self.vidscene)
115+
# TWSS____________
116+
case "TWSS":
117+
await self.bot.obsws.show_and_wait(f'TWSS{random.randint(1, 21)}', '[S] TWSS')
118+
case _:
119+
pass
120+

customalerts/chat.py

+109
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
from poolguy.utils import ColorLogger, re, json
2+
from poolguy.twitchws import Alert
3+
4+
logger = ColorLogger(__name__)
5+
6+
class ChannelChatMessageAlert(Alert):
7+
"""channel.chat.message
8+
{
9+
"broadcaster_user_id": "108284496",
10+
"broadcaster_user_login": "s4w3d0ff",
11+
"broadcaster_user_name": "s4w3d0ff",
12+
"source_broadcaster_user_id": null,
13+
"source_broadcaster_user_login": null,
14+
"source_broadcaster_user_name": null,
15+
"chatter_user_id": "108284496",
16+
"chatter_user_login": "s4w3d0ff",
17+
"chatter_user_name": "s4w3d0ff",
18+
"message_id": "de0d647b-6326-430c-9050-ded5c52bb860",
19+
"source_message_id": null,
20+
"message": {
21+
"text": "sgdsfgh",
22+
"fragments": [
23+
{
24+
"type": "text",
25+
"text": "sgdsfgh",
26+
"cheermote": null,
27+
"emote": null,
28+
"mention": null
29+
}
30+
]
31+
},
32+
"color": "#337272",
33+
"badges": [
34+
{
35+
"set_id": "broadcaster",
36+
"id": "1",
37+
"info": ""
38+
},
39+
{
40+
"set_id": "subscriber",
41+
"id": "18",
42+
"info": "31"
43+
},
44+
{
45+
"set_id": "glitchcon2020",
46+
"id": "1",
47+
"info": ""
48+
}
49+
],
50+
"source_badges": null,
51+
"message_type": "text",
52+
"cheer": null,
53+
"reply": null,
54+
"channel_points_custom_reward_id": null,
55+
"channel_points_animation_id": null
56+
}
57+
"""
58+
async def parseBadges(self):
59+
bid = self.data['source_broadcaster_user_id'] or self.data['broadcaster_user_id']
60+
message_badges = self.data['source_badges'] or self.data['badges']
61+
chatter_id = self.data['chatter_user_id']
62+
if bid not in self.bot.channelBadges:
63+
self.bot.channelBadges[str(bid)] = await self.bot.getChanBadges(bid)
64+
return [self.bot.channelBadges[bid][i['set_id']][i['id']] for i in message_badges]
65+
66+
async def parseEmotesText(self):
67+
etext = self.data['message']['text']
68+
for f in self.data['message']['fragments']:
69+
if f['type'] == 'emote':
70+
etext = re.sub(
71+
r'\b'+re.escape(f['text'])+r'\b',
72+
await self.bot.http.parseTTVEmote(f['emote']['id'], 'animated' if 'animated' in f['emote']['format'] else 'static'),
73+
etext)
74+
return etext
75+
76+
async def process(self):
77+
#logger.debug(f'[Chat] {json.dumps(self.data, indent=2)}', 'purple')
78+
id = self.data['message_id']
79+
message = self.data['message']
80+
badge_urls = await self.parseBadges()
81+
text = await self.parseEmotesText()
82+
out = {
83+
'id': id,
84+
'user': self.data['chatter_user_name'],
85+
'color': self.data['color'],
86+
'badges': badge_urls,
87+
'text': text,
88+
'timestamp': self.timestamp
89+
}
90+
await self.bot.chat_queue.put(out)
91+
logger.debug(f'[Chat] {json.dumps(out, indent=2)}', 'purple')
92+
93+
94+
class ChannelChatNotificationAlert(Alert):
95+
""" channel.chat.notification """
96+
async def process(self):
97+
logger.debug(f'[ChannelChatNotificationAlert] {json.dumps(self.data, indent=2)}', 'purple')
98+
notice_type = self.data['notice_type']
99+
if notice_type.startswith('shared'):
100+
return
101+
out = {
102+
"notice_type": notice_type,
103+
"name": "Anonymous" if self.data['chatter_is_anonymous'] else self.data['chatter_user_name'],
104+
"message": self.data['message'],
105+
"sys_message": self.data['system_message'],
106+
"event": self.data[notice_type]
107+
}
108+
await self.bot.alertws_queue.put(out)
109+
logger.error(f'[ChannelChatNotificationAlert] \n{json.dumps(out, indent=2)}')

0 commit comments

Comments
 (0)