22
33from collections .abc import Iterable , Sequence
44from datetime import datetime
5- from typing import Literal , Protocol
5+ from typing import Literal , Protocol , TypedDict
66
77from ghdcbot .config .models import IdentityMapping
88from ghdcbot .core .models import (
1414)
1515
1616
17+ class IdentityLinkDict (TypedDict , total = False ):
18+ discord_user_id : str
19+ github_user : str
20+ verified : int
21+ verification_code : str | None
22+ expires_at : str | None
23+ created_at : str
24+ verified_at : str | None
25+ unlinked_at : str | None
26+
27+
28+ class UnlinkResultDict (TypedDict ):
29+ discord_user_id : str
30+ github_user : str
31+ verified_at : str
32+ unlinked_at : str
33+
34+
35+ class IdentityStatusDict (TypedDict ):
36+ github_user : str | None
37+ status : Literal ["verified" , "verified_stale" , "pending" , "not_linked" ]
38+ verified_at : str | None
39+ is_stale : bool
40+
41+
42+ class IssueRequestDict (TypedDict ):
43+ request_id : str
44+ discord_user_id : str
45+ github_user : str
46+ owner : str
47+ repo : str
48+ issue_number : int
49+ issue_url : str
50+ created_at : str
51+ status : str
52+
53+
54+ class AuditEventDict (TypedDict , total = False ):
55+ event_type : str
56+ timestamp : str
57+ context : dict
58+
59+
60+ class NotificationRecordDict (TypedDict ):
61+ dedupe_key : str
62+ event_type : str
63+ github_user : str
64+ discord_user_id : str
65+ repo : str
66+ target : str | None
67+ channel_id : str | None
68+ sent_at : str
69+
70+
1771class GitHubReader (Protocol ):
1872 def list_contributions (self , since : datetime ) -> Iterable [ContributionEvent ]:
1973 """Yield contributions since the given timestamp."""
@@ -90,24 +144,30 @@ def create_identity_claim(
90144 ) -> None :
91145 """Create or refresh a pending identity claim for (discord_user_id, github_user)."""
92146
93- def get_identity_link (self , discord_user_id : str , github_user : str ) -> dict | None :
147+ def get_identity_link (
148+ self , discord_user_id : str , github_user : str
149+ ) -> IdentityLinkDict | None :
94150 """Return identity link row for (discord_user_id, github_user), or None."""
95151
96152 def mark_identity_verified (self , discord_user_id : str , github_user : str ) -> None :
97153 """Mark an identity claim as verified."""
98154
99- def unlink_identity (self , discord_user_id : str , cooldown_hours : int ) -> dict | None :
155+ def unlink_identity (
156+ self , discord_user_id : str , cooldown_hours : int
157+ ) -> UnlinkResultDict | None :
100158 """Unlink the verified identity for a Discord user. Returns unlink info or None."""
101159
102160 def list_verified_identity_mappings (self ) -> list [IdentityMapping ]:
103161 """Return all verified identity mappings."""
104162
105- def get_identity_links_for_discord_user (self , discord_user_id : str ) -> list [dict ]:
163+ def get_identity_links_for_discord_user (
164+ self , discord_user_id : str
165+ ) -> list [IdentityLinkDict ]:
106166 """Return all identity link rows for a Discord user (verified and pending)."""
107167
108168 def get_identity_status (
109169 self , discord_user_id : str , max_age_days : int | None = None
110- ) -> dict :
170+ ) -> IdentityStatusDict :
111171 """Return current identity status dict for a Discord user."""
112172
113173 # Issue requests
@@ -124,10 +184,10 @@ def insert_issue_request(
124184 ) -> None :
125185 """Store a new issue assignment request with status pending."""
126186
127- def list_pending_issue_requests (self ) -> list [dict ]:
187+ def list_pending_issue_requests (self ) -> list [IssueRequestDict ]:
128188 """Return all pending issue requests ordered by created_at ascending."""
129189
130- def get_issue_request (self , request_id : str ) -> dict | None :
190+ def get_issue_request (self , request_id : str ) -> IssueRequestDict | None :
131191 """Return a single issue request by request_id, or None."""
132192
133193 def update_issue_request_status (
@@ -139,10 +199,10 @@ def update_issue_request_status(
139199
140200 # Audit log
141201
142- def append_audit_event (self , event : dict ) -> None :
202+ def append_audit_event (self , event : AuditEventDict ) -> None :
143203 """Append an audit event (append-only)."""
144204
145- def list_audit_events (self ) -> list [dict ]:
205+ def list_audit_events (self ) -> list [AuditEventDict ]:
146206 """Return all audit events."""
147207
148208 # Notifications
@@ -160,7 +220,7 @@ def mark_notification_sent(
160220 ) -> None :
161221 """Record that a notification was sent (deduplication tracking)."""
162222
163- def list_recent_notifications (self , limit : int = 1000 ) -> list [dict ]:
223+ def list_recent_notifications (self , limit : int = 1000 ) -> list [NotificationRecordDict ]:
164224 """Return recent sent notifications ordered by sent_at descending."""
165225
166226
0 commit comments