3737 from utils .dota import SteamUserUpdate
3838 from utils .dota .schemas import opendota
3939
40- type ActiveMatch = PlayMatch | WatchMatch | UnsupportedMatch
40+ type ActiveMatch = PlayingMatch | SpectatingMatch | UnsupportedActivity
4141
4242 class ScoreQueryRow (TypedDict ):
4343 friend_id : int
@@ -92,46 +92,36 @@ def __str__(self) -> str:
9292 return f"<{ self .__class__ .__name__ } >"
9393
9494
95- @dataclass (slots = True )
96- class NotInDota (Activity ): ...
97-
98-
99- @dataclass (slots = True )
100- class Transition (Activity ): ...
101-
102-
10395@dataclass (slots = True )
10496class Dashboard (Activity ): ...
10597
10698
10799@dataclass (slots = True )
108- class Playing (Activity ):
100+ class PlayingPartial (Activity ):
109101 watchable_game_id : str
110102
111103
112104@dataclass (slots = True )
113- class Watching (Activity ):
105+ class SpectatingPartial (Activity ):
114106 watching_server : str
115107
116108
117109@dataclass (slots = True )
118- class DemoMode (Activity ): ...
119-
120-
121- @dataclass (slots = True )
122- class BotMatch (Activity ): ...
110+ class UnsupportedPartial (Activity ):
111+ msg : str
123112
124113
125114@dataclass (slots = True )
126- class Replay (Activity ): ...
115+ class NotInDota (Activity ): ...
127116
128117
129118@dataclass (slots = True )
130- class PrivateLobby (Activity ): ...
119+ class Transition (Activity ):
120+ msg : str
131121
132122
133123@dataclass (slots = True )
134- class CustomGames (Activity ): ...
124+ class Unknown (Activity ): ...
135125
136126
137127class RichPresence :
@@ -170,8 +160,8 @@ def __init__(self, bot: IreBot, steam_user: Dota2SteamUser) -> None:
170160 self ._bot : IreBot = bot
171161 self .steam_user : Dota2SteamUser = steam_user
172162 self .rich_presence : RichPresence = RichPresence (steam_user .rich_presence )
173- self .active_match : PlayMatch | WatchMatch | UnsupportedMatch | None = None
174- self .activity : Activity = Transition ()
163+ self .active_match : PlayingMatch | SpectatingMatch | UnsupportedActivity | None = None
164+ self .activity : Activity = Transition ("Haven't received any RP updates yet." )
175165
176166 @override
177167 def __repr__ (self ) -> str :
@@ -230,9 +220,9 @@ def color(self) -> str:
230220
231221def format_match_response (func : Callable [..., Coroutine [Any , Any , str ]]) -> Callable [..., Coroutine [Any , Any , str ]]:
232222 @functools .wraps (func )
233- async def wrapper (self : Match , * args : Any , ** kwargs : Any ) -> str :
234- if isinstance (self , UnsupportedMatch ):
235- return self .activity_tag
223+ async def wrapper (self : LiveMatch , * args : Any , ** kwargs : Any ) -> str :
224+ if isinstance (self , UnsupportedActivity ):
225+ return self .message
236226
237227 prefix = f"[{ self .activity_tag } ] " if self .activity_tag else ""
238228 response = await func (self , * args , ** kwargs )
@@ -241,7 +231,7 @@ async def wrapper(self: Match, *args: Any, **kwargs: Any) -> str:
241231 return wrapper
242232
243233
244- class Match :
234+ class LiveMatch :
245235 def __init__ (
246236 self ,
247237 bot : IreBot ,
@@ -406,7 +396,7 @@ async def server_steam_id_command_response(self) -> str:
406396 return str (self .server_steam_id )
407397
408398
409- class PlayMatch ( Match ):
399+ class PlayingMatch ( LiveMatch ):
410400 def __init__ (self , bot : IreBot , watchable_game_id : str ) -> None :
411401 super ().__init__ (bot )
412402 self .watchable_game_id : str = watchable_game_id
@@ -491,7 +481,7 @@ async def played_with(self, friend_id: int, last_game: MinimalMatch) -> str:
491481 return "No players from the last game present in the match"
492482
493483
494- class WatchMatch ( Match ):
484+ class SpectatingMatch ( LiveMatch ):
495485 def __init__ (self , bot : IreBot , watching_server : str ) -> None :
496486 super ().__init__ (bot , tag = "Spectating" )
497487 self .watching_server : str = watching_server
@@ -538,16 +528,17 @@ async def update_data(self) -> None:
538528 self .update_data .stop ()
539529
540530
541- class UnsupportedMatch ( Match ):
531+ class UnsupportedActivity ( LiveMatch ):
542532 """A class describing unsupported matches.
543533
544534 All chat commands for objects of this type should return unsupported message response.
545535 For example, if streamer is playing Demo Mode, then the bot should only respond with "Demo Mode is not supported",
546536 because, well, there is no data in Demo Mode to insect.
547537 """
548538
549- def __init__ (self , bot : IreBot , tag : str = "" ) -> None :
550- super ().__init__ (bot , tag )
539+ def __init__ (self , bot : IreBot , message : str = "" ) -> None :
540+ super ().__init__ (bot , "" )
541+ self .message = message
551542
552543
553544class Dota2RichPresenceFlow (IrePublicComponent ):
@@ -571,8 +562,8 @@ def __init__(self, bot: IreBot) -> None:
571562 super ().__init__ (bot )
572563 self .friends : dict [int , Friend ] = {}
573564
574- self .play_matches_index : dict [str , PlayMatch ] = {}
575- self .watch_matches_index : dict [str , WatchMatch ] = {}
565+ self .play_matches_index : dict [str , PlayingMatch ] = {}
566+ self .watch_matches_index : dict [str , SpectatingMatch ] = {}
576567
577568 self .debug : bool = True
578569
@@ -636,39 +627,37 @@ async def get_activity(self, friend: Friend) -> Activity:
636627 # something is off
637628 lobby_param0 = rp .raw .get ("param0" ) or "_missing"
638629 lobby_map : dict [str , Activity ] = {
639- LobbyParam0 .DemoMode : DemoMode ( ),
640- LobbyParam0 .BotMatch : BotMatch ( ),
630+ LobbyParam0 .DemoMode : UnsupportedPartial ( "Demo mode is not supported" ),
631+ LobbyParam0 .BotMatch : UnsupportedPartial ( "Bot matches are not supported" ),
641632 }
642- return lobby_map .get (lobby_param0 , Transition ())
633+ return lobby_map .get (lobby_param0 , Transition ("RP is `playing` but watchable_game_id=None" ))
643634
644635 if watchable_game_id == "0" :
645636 # something is off again
646637 # usually this happens when a player has just quit the match into the main menu
647638 # the status flickers for a few seconds to be `watchable_game_id=0`
648- return Transition ()
649- return Playing (watchable_game_id )
639+ return Transition ("RP is `playing` but watchable_game_id=0" )
640+ return PlayingPartial (watchable_game_id )
650641
651642 # Watching
652643 if rp .status in {Status .Spectating , Status .WatchingTournament }:
653644 watching_server = rp .raw .get ("watching_server" )
654645 if watching_server is None :
655- return Replay ()
656- return Watching (watching_server )
657-
658- if rp .status == Status .BotPractice :
659- return DemoMode ()
660-
661- # Private Lobby
662- if rp .status == Status .PrivateLobby :
663- return PrivateLobby ()
664-
665- # Custom games
666- if rp .status == Status .CustomGameLobby :
667- return CustomGames ()
646+ return UnsupportedPartial ("Data in watching replays is not supported" )
647+ return SpectatingPartial (watching_server )
668648
669649 if rp .status == Status .NoStatus :
670650 # usually this happens in exact moment when the player closes Dota
671- return Transition ()
651+ return Transition ("Closed Dota" )
652+
653+ other_statuses = {
654+ Status .BotPractice : "Demo mode is not supported" ,
655+ Status .PrivateLobby : "Bot matches are not supported" ,
656+ Status .CustomGameLobby : "Private lobbies (this includes draft in public lobbies) are not supported" ,
657+ Status .CrownfallNestOfThorns : "Nest of Thorns is not supported."
658+ }
659+ if msg := other_statuses .get (rp .status ):
660+ return UnsupportedPartial (msg )
672661
673662 # Unrecognized
674663 text = (
@@ -681,7 +670,7 @@ async def get_activity(self, friend: Friend) -> Activity:
681670 log .warning (text )
682671 await self .bot .error_webhook .send (content = self .bot .error_ping + "\n " + text )
683672
684- return Transition ()
673+ return Unknown ()
685674
686675 async def analyze_rich_presence (self , friend : Friend ) -> None :
687676 """Analyze Rich Presence.
@@ -718,41 +707,25 @@ async def analyze_rich_presence(self, friend: Friend) -> None:
718707 # no activity changes = no need to do anything
719708 return
720709
721- match new_activity :
722- case Dashboard ():
723- await self .conclude_friend_match (friend )
724- case Playing ():
725- # Friend is in a match as a player
726- if (w_id := new_activity .watchable_game_id ) not in self .play_matches_index :
727- self .play_matches_index [w_id ] = PlayMatch (self .bot , w_id )
728- friend .active_match = self .play_matches_index [w_id ]
729- friend .active_match .friends .add (friend )
730- case Watching ():
731- if (w_s := new_activity .watching_server ) not in self .watch_matches_index :
732- self .watch_matches_index [w_s ] = WatchMatch (self .bot , w_s )
733- friend .active_match = self .watch_matches_index [w_s ]
734- friend .active_match .friends .add (friend )
735- case DemoMode ():
736- friend .active_match = UnsupportedMatch (self .bot , tag = "Demo mode is not supported" )
737- case BotMatch ():
738- friend .active_match = UnsupportedMatch (self .bot , tag = "Bot matches are not supported" )
739- case Replay ():
740- friend .active_match = UnsupportedMatch (self .bot , tag = "Data in watching replays is not supported" )
741- case PrivateLobby ():
742- friend .active_match = UnsupportedMatch (
743- self .bot , tag = "Private lobbies (this includes draft in public lobbies) are not supported"
744- )
745- case CustomGames ():
746- friend .active_match = UnsupportedMatch (
747- self .bot ,
748- tag = "Custom Games Lobbies (in draft stage) are not supported - wait for the game to start." ,
749- )
750- case Transition ():
751- # Wait for confirmed statuses
752- return
753- case _:
754- # Wait for confirmed statuses
755- return
710+ if isinstance (new_activity , Dashboard ):
711+ await self .conclude_friend_match (friend )
712+ elif isinstance (new_activity , PlayingPartial ):
713+ # Friend is in a match as a player
714+ if (w_id := new_activity .watchable_game_id ) not in self .play_matches_index :
715+ self .play_matches_index [w_id ] = PlayingMatch (self .bot , w_id )
716+ friend .active_match = self .play_matches_index [w_id ]
717+ friend .active_match .friends .add (friend )
718+ elif isinstance (new_activity , SpectatingPartial ):
719+ if (w_s := new_activity .watching_server ) not in self .watch_matches_index :
720+ self .watch_matches_index [w_s ] = SpectatingMatch (self .bot , w_s )
721+ friend .active_match = self .watch_matches_index [w_s ]
722+ friend .active_match .friends .add (friend )
723+ elif isinstance (new_activity , UnsupportedPartial ):
724+ friend .active_match = UnsupportedActivity (self .bot , message = new_activity .msg )
725+ else :
726+ # Transition, Unknown or (???)
727+ # wait for confirmed statuses
728+ return
756729
757730 # @commands.Component.listener("steam_user_update")
758731 async def steam_user_update (self , update : SteamUserUpdate ) -> None :
@@ -782,7 +755,7 @@ async def conclude_friend_match(self, friend: Friend) -> None:
782755 This nulls `Friend.active_match` attribute as well as adds the match into the database
783756 if it's a play match.
784757 """
785- if (match := friend .active_match ) and isinstance (match , PlayMatch ) and match .match_id :
758+ if (match := friend .active_match ) and isinstance (match , PlayingMatch ) and match .match_id :
786759 player_slot = next (iter (s for (s , p ) in enumerate (match .players ) if p .friend_id == friend .steam_user .id ), None )
787760 if player_slot is None :
788761 msg = "Somehow `player_slot` is `None` in `conclude_friend_match`"
@@ -960,7 +933,7 @@ async def played_with(self, ctx: IreContext) -> None:
960933 """List recurring players from the last game present in the current match."""
961934 active_match = await self .find_active_match (ctx .broadcaster .id )
962935
963- if isinstance (active_match , PlayMatch ):
936+ if isinstance (active_match , PlayingMatch ):
964937 friend_id , _ , last_game = await self .get_last_game (ctx .broadcaster .id )
965938 response = await active_match .played_with (friend_id , last_game )
966939 else :
@@ -1208,6 +1181,9 @@ async def party(self, ctx: IreContext) -> None:
12081181 msg = "Streamer is not in a party."
12091182 await ctx .send (msg )
12101183 return
1184+ if party2 := friend .rich_presence .raw .get ("party2" ):
1185+ # Apparently if a party is too big, valve just slice the string into party2
1186+ party += party2
12111187
12121188 steam32_ids = [m .id for m in map (steam .ID , PARTY_MEMBERS_PATTERN .findall (party ))]
12131189
@@ -1508,13 +1484,13 @@ async def debug_announce_gc_ready(self) -> None:
15081484 await self .debug_deliver ("Connection to Dota 2 Game Coordinator is ready" )
15091485
15101486 @commands .Component .listener ("heroes_data_ready" )
1511- async def debug_announce_hero_data_ready (self , match : Match ) -> None :
1487+ async def debug_announce_hero_data_ready (self , match : LiveMatch ) -> None :
15121488 """Announce in Irene's twitch chat that match heroes data is ready."""
15131489 if config ["STEAM" ]["IRENE_ID32" ] in [f .steam_user .id for f in match .friends ]:
15141490 await self .debug_deliver (f"Heroes Data for { match .match_id } is ready" )
15151491
15161492 @commands .Component .listener ("players_data_ready" )
1517- async def debug_announce_players_data_ready (self , match : Match ) -> None :
1493+ async def debug_announce_players_data_ready (self , match : LiveMatch ) -> None :
15181494 """Announce in Irene's twitch chat that match players data is ready."""
15191495 if config ["STEAM" ]["IRENE_ID32" ] in [f .steam_user .id for f in match .friends ]:
15201496 await self .debug_deliver (f"Players Data for { match .match_id } is ready" )
0 commit comments