|
| 1 | +use crate::{commands::ERROR_COLOUR, Data}; |
| 2 | +use poise::serenity_prelude::{ |
| 3 | + self as serenity, ChannelId, CreateEmbed, CreateMessage, GuildChannel, |
| 4 | +}; |
| 5 | + |
| 6 | +pub async fn thread_create(ctx: &serenity::Context, data: &Data, thread: &GuildChannel) { |
| 7 | + // TODO: make this an environment variable? |
| 8 | + if thread.parent_id != Some(ChannelId::from(1149679256981479464)) { |
| 9 | + return; |
| 10 | + } |
| 11 | + |
| 12 | + // Already responded to this thread. |
| 13 | + if data.forum_threads.read().unwrap().contains(&thread.id) { |
| 14 | + return; |
| 15 | + } |
| 16 | + |
| 17 | + // This is jank until serenity 0.13 or switching tablet bot to serenity@next. |
| 18 | + // This will prevent the bot from responding if it just joins the thread without it being a new thread. |
| 19 | + if chrono::Utc::now() - *thread.id.created_at() > chrono::Duration::seconds(5) { |
| 20 | + return; |
| 21 | + } |
| 22 | + |
| 23 | + data.forum_threads.write().unwrap().insert(thread.id); |
| 24 | + collect_and_action(ctx, thread).await; |
| 25 | +} |
| 26 | + |
| 27 | +macro_rules! check_device { |
| 28 | + ($device:expr, $vendor_id:expr, $pattern:pat $(if $guard:expr)?) => { |
| 29 | + (($device.vendor_id == $vendor_id) && matches!($device.product_id, $pattern $(if $guard)?)) |
| 30 | + }; |
| 31 | +} |
| 32 | + |
| 33 | +async fn collect_and_action(ctx: &serenity::Context, thread: &GuildChannel) { |
| 34 | + if let Some(message) = serenity::MessageCollector::new(ctx) |
| 35 | + .timeout(std::time::Duration::from_secs(10)) |
| 36 | + .channel_id(thread.id) |
| 37 | + .await |
| 38 | + { |
| 39 | + println!("{}", message.id); |
| 40 | + |
| 41 | + if message.attachments.is_empty() { |
| 42 | + let desc = "Sending diagnostics is a mandatory step! Please follow the instructions \ |
| 43 | + below or this request will be deleted.\n\n- Start OpenTabletDriver (if it \ |
| 44 | + is not already running)\n- Go to `Help` -> `Export Diagnostics` in the \ |
| 45 | + top menu\n- Save the file, then upload here."; |
| 46 | + |
| 47 | + let embed = CreateEmbed::new() |
| 48 | + .title("Exporting diagnostics") |
| 49 | + .description(desc) |
| 50 | + .color(ERROR_COLOUR); |
| 51 | + |
| 52 | + let _ = thread |
| 53 | + .id |
| 54 | + .send_message(ctx, CreateMessage::new().embed(embed)) |
| 55 | + .await; |
| 56 | + return; |
| 57 | + } |
| 58 | + |
| 59 | + for attachment in message.attachments { |
| 60 | + // 5MB, unlikely to be a diagnostic and massive if it is one. |
| 61 | + if attachment.size > 5000000 { |
| 62 | + println!("big attachment"); |
| 63 | + continue; |
| 64 | + } |
| 65 | + |
| 66 | + let will_eval = match attachment.content_type { |
| 67 | + Some(ref s) if s.contains("application/json") => true, |
| 68 | + Some(ref s) if s.contains("text/plain") => true, |
| 69 | + // user likely stripped the extension |
| 70 | + None => true, |
| 71 | + // something else, likely an image |
| 72 | + _ => false, |
| 73 | + }; |
| 74 | + |
| 75 | + if will_eval { |
| 76 | + let Ok(data) = attachment.download().await else { |
| 77 | + println!("Failed to download attachment, message was likely deleted."); |
| 78 | + return; |
| 79 | + }; |
| 80 | + |
| 81 | + let Ok(diag) = serde_json::from_slice::<MinimifiedDiagSchema>(&data) else { |
| 82 | + println!("Could not parse attachment as diagnostic."); |
| 83 | + continue; |
| 84 | + }; |
| 85 | + |
| 86 | + let mut maybe_dump = None; |
| 87 | + // Checks if it is a known problematic device that needs a string dump |
| 88 | + // Right now it just ends at the first mention of a problematic device, but not |
| 89 | + // many people ask for config support when they have multiple tablets plugged in? |
| 90 | + for device in diag.hid_devices { |
| 91 | + if check_device!(device, 21827, 129) |
| 92 | + || check_device!(device, 9580, 97 | 100 | 109 | 110 | 111) |
| 93 | + { |
| 94 | + maybe_dump = Some(device); |
| 95 | + break; |
| 96 | + } |
| 97 | + } |
| 98 | + |
| 99 | + if let Some(device) = maybe_dump { |
| 100 | + let description = format!( |
| 101 | + "Your device is known to have tricky identifiers to work with, and such a \ |
| 102 | + device string dump will help support this tablet faster. Please follow \ |
| 103 | + these instructions below.\n\n- Start OpenTabletDriver (if it is not \ |
| 104 | + already running)\n- Go to `Tablets` -> `Device string reader` in the top \ |
| 105 | + menu\n- Put `{}` in the top box\n- `{}` in the middle box\n- Press `Dump \ |
| 106 | + all`\n- Save the file, then upload here.", |
| 107 | + device.vendor_id, device.product_id |
| 108 | + ); |
| 109 | + |
| 110 | + let embed = CreateEmbed::new() |
| 111 | + .title("String dump required") |
| 112 | + .description(description) |
| 113 | + .color(ERROR_COLOUR); |
| 114 | + |
| 115 | + let _ = thread |
| 116 | + .send_message(ctx, CreateMessage::new().embed(embed)) |
| 117 | + .await; |
| 118 | + break; |
| 119 | + } |
| 120 | + } |
| 121 | + } |
| 122 | + } |
| 123 | +} |
| 124 | + |
| 125 | +#[derive(serde::Deserialize)] |
| 126 | +struct MinimifiedDiagSchema { |
| 127 | + #[serde(rename = "HID Devices")] |
| 128 | + hid_devices: Vec<HidDevice>, |
| 129 | +} |
| 130 | + |
| 131 | +#[derive(serde::Deserialize)] |
| 132 | +#[serde(rename_all = "PascalCase")] |
| 133 | +struct HidDevice { |
| 134 | + #[serde(rename = "VendorID")] |
| 135 | + vendor_id: i16, |
| 136 | + #[serde(rename = "ProductID")] |
| 137 | + product_id: i16, |
| 138 | + // I will put these in when I have a "surefire" way of getting the intended device. |
| 139 | + // I will probably use them for checking if udev rules are installed (to get the right lengths) |
| 140 | + //input_report_length: i16, |
| 141 | + //output_report_length: i16, |
| 142 | +} |
0 commit comments