11use crate :: db:: repr:: { ApiKey , Permission } ;
22use crate :: prelude:: api:: * ;
3- use crate :: types:: BotState ;
3+ use crate :: prelude:: db:: * ;
4+ use crate :: prelude:: i18n:: * ;
5+ use crate :: prelude:: utils:: * ;
6+ use crate :: types:: { BotCommand , BotState } ;
47use axum:: Json ;
58use axum:: extract:: { Extension , State } ;
69use axum:: http:: StatusCode ;
710use rustmail_types:: CreateTicket ;
11+ use serenity:: all:: { ChannelId , CreateChannel , GuildId , UserId } ;
12+ use std:: collections:: HashMap ;
813use std:: sync:: Arc ;
914use tokio:: sync:: Mutex ;
1015
@@ -16,26 +21,192 @@ pub async fn handle_external_ticket_create(
1621 check_permission ( & api_key, Permission :: CreateTicket )
1722 . map_err ( |e| ( StatusCode :: FORBIDDEN , format ! ( "{:?}" , e) ) ) ?;
1823
19- let _current_config = {
24+ let user_id_u64 = update. discord_id . parse :: < u64 > ( ) . map_err ( |_| {
25+ (
26+ StatusCode :: BAD_REQUEST ,
27+ "Invalid Discord ID format" . to_string ( ) ,
28+ )
29+ } ) ?;
30+
31+ let user_id = UserId :: new ( user_id_u64) ;
32+
33+ let ( config, db_pool, bot_http, command_tx) = {
2034 let state = bot_state. lock ( ) . await ;
21- match & state. config {
22- Some ( c) => c. clone ( ) ,
23- None => {
24- return Err ( (
25- StatusCode :: INTERNAL_SERVER_ERROR ,
26- "Configuration not loaded" . to_string ( ) ,
27- ) ) ;
28- }
29- }
35+ let config = state
36+ . config
37+ . as_ref ( )
38+ . ok_or ( (
39+ StatusCode :: INTERNAL_SERVER_ERROR ,
40+ "Configuration not loaded" . to_string ( ) ,
41+ ) ) ?
42+ . clone ( ) ;
43+ let db_pool = state
44+ . db_pool
45+ . as_ref ( )
46+ . ok_or ( (
47+ StatusCode :: INTERNAL_SERVER_ERROR ,
48+ "Database not available" . to_string ( ) ,
49+ ) ) ?
50+ . clone ( ) ;
51+ let bot_http = state
52+ . bot_http
53+ . as_ref ( )
54+ . ok_or ( (
55+ StatusCode :: INTERNAL_SERVER_ERROR ,
56+ "Bot HTTP client not available" . to_string ( ) ,
57+ ) ) ?
58+ . clone ( ) ;
59+ let command_tx = state. command_tx . clone ( ) ;
60+
61+ ( config, db_pool, bot_http, command_tx)
3062 } ;
3163
3264 println ! (
33- "API Key #{} creating ticket for Discord ID: {:?}" ,
34- api_key. id, update. discord_id
65+ "API Key #{} creating ticket for Discord ID: {}" ,
66+ api_key. id, user_id_u64
67+ ) ;
68+
69+ let user = bot_http. get_user ( user_id) . await . map_err ( |e| {
70+ (
71+ StatusCode :: NOT_FOUND ,
72+ format ! ( "Discord user not found: {}" , e) ,
73+ )
74+ } ) ?;
75+
76+ if user. bot {
77+ return Err ( (
78+ StatusCode :: BAD_REQUEST ,
79+ "Cannot create ticket for bot users" . to_string ( ) ,
80+ ) ) ;
81+ }
82+
83+ let ( tx, rx) = tokio:: sync:: oneshot:: channel ( ) ;
84+ command_tx
85+ . send ( BotCommand :: CheckUserIsMember {
86+ user_id : user_id_u64,
87+ resp : tx,
88+ } )
89+ . await
90+ . map_err ( |_| {
91+ (
92+ StatusCode :: INTERNAL_SERVER_ERROR ,
93+ "Failed to communicate with bot" . to_string ( ) ,
94+ )
95+ } ) ?;
96+
97+ let is_member = rx. await . map_err ( |_| {
98+ (
99+ StatusCode :: INTERNAL_SERVER_ERROR ,
100+ "Failed to check guild membership" . to_string ( ) ,
101+ )
102+ } ) ?;
103+
104+ if !is_member {
105+ return Err ( (
106+ StatusCode :: FORBIDDEN ,
107+ "User is not a member of the community guild" . to_string ( ) ,
108+ ) ) ;
109+ }
110+
111+ if thread_exists ( user_id, & db_pool) . await {
112+ return if let Some ( channel_id_str) = get_thread_channel_by_user_id ( user_id, & db_pool) . await
113+ {
114+ Err ( (
115+ StatusCode :: CONFLICT ,
116+ format ! ( "User already has an active ticket: <#{}>" , channel_id_str) ,
117+ ) )
118+ } else {
119+ Err ( (
120+ StatusCode :: CONFLICT ,
121+ "User already has an active ticket" . to_string ( ) ,
122+ ) )
123+ } ;
124+ }
125+
126+ let username = user. name . clone ( ) ;
127+ let thread_name = format ! ( "🔴・{}・0m" , username) ;
128+ let staff_guild_id = GuildId :: new ( config. bot . get_staff_guild_id ( ) ) ;
129+ let inbox_category_id = ChannelId :: new ( config. thread . inbox_category_id ) ;
130+
131+ let channel_builder = CreateChannel :: new ( & thread_name) . category ( inbox_category_id) ;
132+
133+ let channel = staff_guild_id
134+ . create_channel ( & bot_http, channel_builder)
135+ . await
136+ . map_err ( |e| {
137+ (
138+ StatusCode :: INTERNAL_SERVER_ERROR ,
139+ format ! ( "Failed to create Discord channel: {}" , e) ,
140+ )
141+ } ) ?;
142+
143+ create_thread_for_user ( & channel, user_id_u64 as i64 , & username, & db_pool)
144+ . await
145+ . map_err ( |e| {
146+ let http_clone = bot_http. clone ( ) ;
147+ let channel_id = channel. id ;
148+ tokio:: spawn ( async move {
149+ let _ = http_clone. delete_channel ( channel_id, None ) . await ;
150+ } ) ;
151+ (
152+ StatusCode :: INTERNAL_SERVER_ERROR ,
153+ format ! ( "Failed to create thread record: {}" , e) ,
154+ )
155+ } ) ?;
156+
157+ let community_guild_id = GuildId :: new ( config. bot . get_community_guild_id ( ) ) ;
158+ let member_join_date = community_guild_id
159+ . member ( & bot_http, user_id)
160+ . await
161+ . ok ( )
162+ . and_then ( |m| m. joined_at )
163+ . map ( |dt| dt. format ( "%Y-%m-%d" ) . to_string ( ) )
164+ . unwrap_or_else ( || "Unknown" . to_string ( ) ) ;
165+
166+ let logs_count = match get_logs_from_user_id ( & user_id. to_string ( ) , & db_pool) . await {
167+ Ok ( logs) => logs. len ( ) ,
168+ Err ( _) => 0 ,
169+ } ;
170+
171+ let params = {
172+ let mut p = HashMap :: new ( ) ;
173+ p. insert ( "logs_count" . to_string ( ) , logs_count. to_string ( ) ) ;
174+ p. insert ( "prefix" . to_string ( ) , config. command . prefix . clone ( ) ) ;
175+ p
176+ } ;
177+
178+ let logs_info = get_translated_message (
179+ & config,
180+ "new_thread.show_logs" ,
181+ Some ( & params) ,
182+ None ,
183+ None ,
184+ None ,
185+ )
186+ . await ;
187+
188+ let open_thread_message = get_user_recap ( user_id, & username, & member_join_date, & logs_info) ;
189+
190+ if let Err ( e) = channel. id . say ( & bot_http, open_thread_message) . await {
191+ eprintln ! ( "Failed to send welcome message to thread: {}" , e) ;
192+ }
193+
194+ if let Ok ( dm_channel) = user. create_dm_channel ( & bot_http) . await {
195+ if let Err ( e) = dm_channel. say ( & bot_http, & config. bot . welcome_message ) . await {
196+ eprintln ! ( "Failed to send DM to user: {}" , e) ;
197+ }
198+ }
199+
200+ println ! (
201+ "API Key #{} successfully created ticket for user {} (channel: {})" ,
202+ api_key. id, username, channel. id
35203 ) ;
36204
37205 Ok ( Json ( serde_json:: json!( {
38- "status" : "ticket created" ,
39- "message" : "Ticket creation endpoint - implementation pending"
206+ "success" : true ,
207+ "channel_id" : channel. id. to_string( ) ,
208+ "user_id" : user_id_u64. to_string( ) ,
209+ "username" : username,
210+ "message" : "Ticket created successfully"
40211 } ) ) )
41212}
0 commit comments