Skip to content

Commit bb4c832

Browse files
committed
Add config-creation forum autoresponse
1 parent 1926b14 commit bb4c832

File tree

6 files changed

+168
-11
lines changed

6 files changed

+168
-11
lines changed

Cargo.lock

+6-3
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -23,3 +23,4 @@ reqwest = "0.11.22"
2323
hex = "0.4.3"
2424
to-arraystring = "0.1.3"
2525
arrayvec = "0.7.4"
26+
chrono = "0.4.39"

src/events/config_creation.rs

+142
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
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+
}

src/events/issues/mod.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -80,10 +80,10 @@ pub async fn message(data: &Data, ctx: &Context, message: &Message) {
8080
.timeout(Duration::from_secs(60))
8181
.await
8282
{
83-
let has_perms = press.member.as_ref().map_or(false, |member| {
84-
member.permissions.map_or(false, |member_perms| {
85-
member_perms.contains(Permissions::MANAGE_MESSAGES)
86-
})
83+
let has_perms = press.member.as_ref().is_some_and(|member| {
84+
member
85+
.permissions
86+
.is_some_and(|member_perms| member_perms.contains(Permissions::MANAGE_MESSAGES))
8787
});
8888

8989
// Users who do not own the message or have permissions cannot execute the interactions.

src/events/mod.rs

+4-1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ use poise::serenity_prelude as serenity;
33
use crate::{Data, Error};
44

55
pub mod code;
6+
pub mod config_creation;
67
pub mod issues;
78

89
pub async fn event_handler(
@@ -11,14 +12,16 @@ pub async fn event_handler(
1112
_framework: poise::FrameworkContext<'_, Data, Error>,
1213
data: &Data,
1314
) -> Result<(), Error> {
14-
#[allow(clippy::single_match)]
1515
match event {
1616
serenity::FullEvent::Message { new_message } => {
1717
if !new_message.author.bot && new_message.guild_id.is_some() {
1818
issues::message(data, ctx, new_message).await;
1919
code::message(ctx, new_message).await;
2020
}
2121
}
22+
serenity::FullEvent::ThreadCreate { thread } => {
23+
config_creation::thread_create(ctx, data, thread).await;
24+
}
2225
_ => (),
2326
}
2427

src/main.rs

+11-3
Original file line numberDiff line numberDiff line change
@@ -11,17 +11,20 @@ pub(crate) mod events;
1111
pub(crate) mod formatting;
1212
pub(crate) mod structures;
1313

14+
use std::collections::HashSet;
1415
use std::sync::RwLock;
1516
use std::time::Duration;
1617
use std::{env, sync::Arc};
1718

1819
use octocrab::Octocrab;
19-
use poise::serenity_prelude::{self as serenity, GatewayIntents};
20+
use poise::serenity_prelude::{self as serenity, ChannelId, GatewayIntents};
2021
use structures::BotState;
2122

2223
pub struct Data {
2324
pub octocrab: Arc<Octocrab>,
2425
pub state: RwLock<BotState>,
26+
/// Small manual cache for threads we have already responded to.
27+
pub forum_threads: RwLock<HashSet<ChannelId>>,
2528
}
2629
type Error = Box<dyn std::error::Error + Send + Sync>;
2730
type Context<'a> = poise::Context<'a, Data, Error>;
@@ -119,13 +122,18 @@ async fn main() {
119122
Box::pin(async move {
120123
println!("Logged in as {}", ready.user.name);
121124
poise::builtins::register_globally(ctx, &framework.options().commands).await?;
122-
Ok(Data { octocrab, state })
125+
Ok(Data {
126+
octocrab,
127+
state,
128+
forum_threads: RwLock::new(HashSet::new()),
129+
})
123130
})
124131
});
125132

126133
let intents = GatewayIntents::GUILD_MESSAGES
127134
| GatewayIntents::DIRECT_MESSAGES
128-
| GatewayIntents::MESSAGE_CONTENT;
135+
| GatewayIntents::MESSAGE_CONTENT
136+
| GatewayIntents::GUILDS;
129137

130138
let mut client = serenity::Client::builder(discord_token, intents)
131139
.framework(framework)

0 commit comments

Comments
 (0)