dircord/src/main.rs

756 lines
26 KiB
Rust
Raw Normal View History

2021-12-27 17:33:09 -06:00
use std::{collections::HashMap, env, fs::File, io::Read, sync::Arc};
2021-12-26 15:13:09 -06:00
2021-12-26 16:05:53 -06:00
use serenity::{
async_trait,
2021-12-27 11:24:31 -06:00
futures::StreamExt,
2021-12-26 16:05:53 -06:00
http::Http,
model::{
channel::Message,
2021-12-27 17:33:09 -06:00
guild::Member,
2022-01-23 19:08:07 -06:00
id::{ChannelId, GuildId, RoleId, UserId},
2021-12-27 17:33:09 -06:00
prelude::Ready,
webhook::Webhook,
2021-12-26 16:05:53 -06:00
},
prelude::*,
2021-12-27 11:24:31 -06:00
Client as DiscordClient,
};
use tokio::sync::Mutex;
2021-12-27 11:24:31 -06:00
use irc::{
client::{data::Config, Client as IrcClient, Sender},
proto::Command,
2021-12-26 16:05:53 -06:00
};
2021-12-26 15:13:09 -06:00
use lazy_static::lazy_static;
2022-01-06 13:29:12 -06:00
use regex::Regex;
2022-01-18 16:55:05 -06:00
use pulldown_cmark::Parser;
use serde::Deserialize;
#[derive(Deserialize)]
struct DircordConfig {
token: String,
webhook: Option<String>,
nickname: Option<String>,
server: String,
port: Option<u16>,
2022-01-16 19:03:50 -06:00
channel: String,
mode: Option<String>,
tls: Option<bool>,
2022-01-16 19:03:50 -06:00
channel_id: u64,
}
2021-12-27 13:49:54 -06:00
2021-12-26 15:13:09 -06:00
struct Handler;
#[async_trait]
impl EventHandler for Handler {
async fn message(&self, ctx: Context, msg: Message) {
let nick = {
if let Some(member) = msg.member {
match member.nick {
Some(n) => n,
2021-12-26 16:05:53 -06:00
None => msg.author.name,
2021-12-26 15:13:09 -06:00
}
} else {
msg.author.name
}
};
2022-01-15 11:15:08 -06:00
let byte = nick.chars().nth(0).unwrap() as u8;
let colour_index = (byte as usize + nick.len()) % 12;
let formatted = format!("\x03{:02}", colour_index);
let mut new_nick = String::with_capacity(formatted.len() + nick.len() + 2);
new_nick.push_str(&formatted);
new_nick.push_str(&nick);
new_nick.push('\x0F');
2022-01-16 19:04:15 -06:00
new_nick = format!(
"{}\u{200B}{}",
&new_nick[..formatted.len() + 1],
&new_nick[formatted.len() + 1..]
);
let nick_bytes = new_nick.len() + 3; // +1 for the space and +2 for the <>
2022-01-15 15:51:54 -06:00
let content_limit = 510 - nick_bytes;
2022-01-16 19:03:50 -06:00
let (user_id, sender, members, channel_id, channel) = {
2021-12-26 15:35:49 -06:00
let data = ctx.data.read().await;
2021-12-26 16:05:53 -06:00
let user_id = data.get::<UserIdKey>().unwrap().to_owned();
2021-12-27 13:28:35 -06:00
let sender = data.get::<SenderKey>().unwrap().to_owned();
let members = data.get::<MembersKey>().unwrap().to_owned();
2022-01-15 15:26:33 -06:00
let channel_id = data.get::<ChannelIdKey>().unwrap().to_owned();
2022-01-16 19:03:50 -06:00
let channel = data.get::<StringKey>().unwrap().to_owned();
2021-12-26 15:35:49 -06:00
2022-01-16 19:03:50 -06:00
(user_id, sender, members, channel_id, channel)
2021-12-26 15:35:49 -06:00
};
2021-12-27 17:33:09 -06:00
2022-01-05 13:26:19 -06:00
let attachments: Vec<String> = msg.attachments.iter().map(|a| a.url.clone()).collect();
2022-01-18 09:42:14 -06:00
if msg.content.starts_with("```") {
let mut lines = msg.content.split("\n").collect::<Vec<&str>>();
// remove the backticks
lines.remove(lines.len() - 1);
lines.remove(0);
if user_id != msg.author.id && !msg.author.bot && msg.channel_id == channel_id {
for line in lines {
send_irc_message(&sender, &channel, &format!("<{}> {}", new_nick, line))
.await
.unwrap();
}
for attachment in attachments {
send_irc_message(&sender, &channel, &format!("<{}> {}", new_nick, attachment))
.await
.unwrap();
}
}
return;
}
lazy_static! {
static ref PING_RE_1: Regex = Regex::new(r"<@[0-9]+>").unwrap();
static ref PING_RE_2: Regex = Regex::new(r"<@![0-9]+>").unwrap();
2022-01-23 10:54:50 -06:00
static ref EMOJI_RE: Regex = Regex::new(r"<:\w+:[0-9]+>").unwrap();
2022-01-23 18:07:34 -06:00
static ref CHANNEL_RE: Regex = Regex::new(r"<#[0-9]+>").unwrap();
2022-01-23 19:08:07 -06:00
static ref ROLE_RE: Regex = Regex::new(r"<@&[0-9]+>").unwrap();
}
2022-01-23 19:08:07 -06:00
let roles = channel_id
.to_channel(&ctx)
.await
.unwrap()
.guild()
.unwrap()
.guild_id
.roles(&ctx)
.await
.unwrap();
let mut id_cache: HashMap<u64, String> = HashMap::new();
if PING_RE_1.is_match(&msg.content) {
for mat in PING_RE_1.find_iter(&msg.content) {
2022-01-15 12:32:47 -06:00
let slice = &msg.content[mat.start() + 2..mat.end() - 1];
let id = slice.parse::<u64>().unwrap();
for member in &*members.lock().await {
if id == member.user.id.0 {
let nick = {
match &member.nick {
Some(n) => n.clone(),
2022-01-15 12:32:47 -06:00
None => member.user.name.clone(),
}
};
id_cache.insert(id, nick);
}
}
}
} else if PING_RE_2.is_match(&msg.content) {
for mat in PING_RE_2.find_iter(&msg.content) {
2022-01-15 12:32:47 -06:00
let slice = &msg.content[mat.start() + 3..mat.end() - 1];
let id = slice.parse::<u64>().unwrap();
for member in &*members.lock().await {
if id == member.user.id.0 {
let nick = {
match &member.nick {
Some(n) => n.clone(),
2022-01-15 12:32:47 -06:00
None => member.user.name.clone(),
}
};
id_cache.insert(id, nick);
}
}
}
}
2022-01-18 16:55:05 -06:00
let mut computed = String::new();
2022-01-21 10:02:08 -06:00
let mut replaced = msg.content.clone();
for mat in PING_RE_1.find_iter(&msg.content) {
let slice = &msg.content[mat.start() + 2..mat.end() - 1];
let id = slice.parse::<u64>().unwrap();
if let Some(cached) = id_cache.get(&id) {
replaced = PING_RE_1
.replace(&replaced, format!("@{}", cached))
.to_string();
}
}
for mat in PING_RE_2.find_iter(&msg.content) {
let slice = &msg.content[mat.start() + 3..mat.end() - 1];
let id = slice.parse::<u64>().unwrap();
if let Some(cached) = id_cache.get(&id) {
replaced = PING_RE_2
.replace(&replaced, format!("@{}", cached))
.to_string();
}
}
2022-01-18 16:55:05 -06:00
2022-01-23 10:54:50 -06:00
for mat in EMOJI_RE.find_iter(&msg.content) {
let slice = &msg.content[mat.start()..mat.end()];
let parts = slice.split(':').collect::<Vec<&str>>();
let formatted = format!(":{}:", parts[1]); // ignore the opening bracket in [0]
replaced = EMOJI_RE.replace(&replaced, formatted).to_string();
}
2022-01-23 18:07:34 -06:00
for mat in CHANNEL_RE.find_iter(&msg.content) {
use serenity::model::channel::Channel::*;
2022-01-23 18:40:51 -06:00
2022-01-23 18:07:34 -06:00
let slice = &msg.content[mat.start() + 2..mat.end() - 1];
let parsed: u64 = slice.parse().unwrap();
let mentioned_channel_id = ChannelId(parsed);
if let Ok(chan) = mentioned_channel_id.to_channel(&ctx).await {
match chan {
2022-01-23 18:40:51 -06:00
Private(_) => {
replaced = CHANNEL_RE
.replace(&replaced, "#invalid-channel")
.to_string()
}
Guild(gc) => {
replaced = CHANNEL_RE
.replace(&replaced, format!("#{}", gc.name))
.to_string()
}
Category(cat) => {
replaced = CHANNEL_RE
.replace(&replaced, format!("#{}", cat.name))
.to_string()
}
_ => {
replaced = CHANNEL_RE
.replace(&replaced, "#invalid-channel")
.to_string()
}
2022-01-23 18:07:34 -06:00
};
} else {
2022-01-23 18:40:51 -06:00
replaced = CHANNEL_RE
.replace(&replaced, "#invalid-channel")
.to_string();
2022-01-23 18:07:34 -06:00
}
}
2022-01-23 19:08:07 -06:00
for mat in ROLE_RE.find_iter(&msg.content) {
let slice = &msg.content[mat.start() + 3..mat.end() - 1];
let parsed: u64 = slice.parse().unwrap();
let pinged_id = RoleId(parsed);
if let Some(role) = roles.get(&pinged_id) {
replaced = ROLE_RE
.replace(&replaced, format!("@{}", role.name))
.to_string();
}
}
2022-01-18 16:55:05 -06:00
{
use pulldown_cmark::Event::*;
use pulldown_cmark::Tag::*;
2022-01-21 10:02:08 -06:00
let parser = Parser::new(&replaced);
2022-01-18 16:55:05 -06:00
for event in parser {
match event {
2022-01-20 08:54:51 -06:00
Text(t) | Html(t) | Code(t) => computed.push_str(&format!("{} ", &t)),
2022-01-18 16:55:05 -06:00
End(_) => computed.push('\x0F'),
Start(tag) => match tag {
Emphasis => computed.push('\x1D'),
Strong => computed.push('\x02'),
Link(_, dest, _) => computed.push_str(&dest),
_ => {}
},
_ => {}
}
}
}
let chars = computed.chars().collect::<Vec<char>>();
2022-01-15 15:51:54 -06:00
let chunks = chars.chunks(content_limit);
2022-01-15 15:26:33 -06:00
if user_id != msg.author.id && !msg.author.bot && msg.channel_id == channel_id {
2022-01-19 15:40:48 -06:00
if let Some(reply) = msg.referenced_message {
2022-01-21 13:50:30 -06:00
let rnick = {
if let Some(member) = reply.member {
match member.nick {
Some(n) => n,
None => reply.author.name,
}
} else {
reply.author.name
}
};
let byte = rnick.chars().nth(0).unwrap() as u8;
let colour_index = (byte as usize + rnick.len()) % 12;
let formatted = format!("\x03{:02}", colour_index);
let mut reply_nick = String::with_capacity(formatted.len() + rnick.len() + 2);
reply_nick.push_str(&formatted);
reply_nick.push_str(&rnick);
reply_nick.push('\x0F');
reply_nick = format!(
"{}\u{200B}{}",
&reply_nick[..formatted.len() + 1],
&reply_nick[formatted.len() + 1..]
);
2022-01-19 15:40:48 -06:00
let mut content = reply.content;
2022-01-19 15:54:13 -06:00
content = content.replace('\n', " ");
content = content.replace("\r\n", " "); // just in case
2022-01-21 13:50:30 -06:00
let reply_nick_bytes = reply_nick.len() + 3;
let reply_content_limit = 510 - reply_nick_bytes - 5;
let to_send = if content.len() > reply_content_limit {
content.truncate(reply_content_limit);
2022-01-19 15:40:48 -06:00
format!("> {}...", content)
} else {
format!("> {}", content)
};
2022-01-21 13:50:30 -06:00
send_irc_message(&sender, &channel, &format!("<{}> {}", reply_nick, to_send))
2022-01-19 15:40:48 -06:00
.await
.unwrap();
}
2022-01-15 15:51:54 -06:00
for chunk in chunks {
let to_send = String::from_iter(chunk.iter());
2022-01-16 19:03:50 -06:00
send_irc_message(&sender, &channel, &format!("<{}> {}", new_nick, to_send))
2022-01-15 15:51:54 -06:00
.await
.unwrap();
}
2022-01-05 13:26:19 -06:00
for attachment in attachments {
2022-01-16 19:03:50 -06:00
send_irc_message(&sender, &channel, &format!("<{}> {}", new_nick, attachment))
2022-01-05 13:26:19 -06:00
.await
.unwrap();
}
2021-12-27 13:28:35 -06:00
}
2021-12-26 16:05:53 -06:00
}
async fn ready(&self, ctx: Context, info: Ready) {
let id = info.user.id;
let mut data = ctx.data.write().await;
data.insert::<UserIdKey>(id);
2021-12-26 15:13:09 -06:00
}
async fn guild_member_addition(&self, ctx: Context, _: GuildId, new_member: Member) {
let members = {
let data = ctx.data.read().await;
let members = data.get::<MembersKey>().unwrap().to_owned();
members
};
members.lock().await.push(new_member);
}
2021-12-26 14:04:47 -06:00
}
2021-12-26 15:13:09 -06:00
2021-12-26 15:35:49 -06:00
struct HttpKey;
struct ChannelIdKey;
2021-12-26 16:05:53 -06:00
struct UserIdKey;
2021-12-27 11:24:31 -06:00
struct SenderKey;
struct MembersKey;
2022-01-16 19:03:50 -06:00
struct StringKey;
2021-12-26 15:35:49 -06:00
impl TypeMapKey for HttpKey {
type Value = Arc<Http>;
}
impl TypeMapKey for ChannelIdKey {
type Value = ChannelId;
}
2021-12-26 16:05:53 -06:00
impl TypeMapKey for UserIdKey {
type Value = UserId;
}
2021-12-27 11:24:31 -06:00
impl TypeMapKey for SenderKey {
type Value = Sender;
}
impl TypeMapKey for MembersKey {
type Value = Arc<Mutex<Vec<Member>>>;
}
2022-01-16 19:03:50 -06:00
impl TypeMapKey for StringKey {
type Value = String;
}
2021-12-26 15:13:09 -06:00
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let filename = env::args().nth(1).unwrap_or(String::from("config.toml"));
2021-12-27 13:49:54 -06:00
let mut data = String::new();
File::open(filename)?.read_to_string(&mut data)?;
let conf: DircordConfig = toml::from_str(&data)?;
2021-12-26 15:13:09 -06:00
let mut discord_client = DiscordClient::builder(&conf.token)
2021-12-27 11:24:31 -06:00
.event_handler(Handler)
.await?;
2021-12-26 15:13:09 -06:00
2022-01-16 19:03:50 -06:00
let channel_id = ChannelId(conf.channel_id);
2021-12-26 15:13:09 -06:00
2021-12-27 11:24:31 -06:00
let config = Config {
nickname: conf.nickname,
server: Some(conf.server),
port: conf.port,
2022-01-16 19:03:50 -06:00
channels: vec![conf.channel.clone()],
use_tls: conf.tls,
umodes: conf.mode,
2021-12-27 11:24:31 -06:00
..Config::default()
};
2021-12-27 13:28:35 -06:00
let irc_client = IrcClient::from_config(config).await?;
2021-12-27 11:24:31 -06:00
let http = discord_client.cache_and_http.http.clone();
let members = Arc::new(Mutex::new(
2022-01-15 12:32:47 -06:00
channel_id
.to_channel(discord_client.cache_and_http.clone())
.await?
.guild()
.unwrap() // we can panic here because if it's not a guild channel then the bot shouldn't even work
.guild_id
.members(&http, None, None)
.await?,
));
2021-12-27 17:33:09 -06:00
2021-12-26 15:35:49 -06:00
{
2021-12-27 11:24:31 -06:00
let mut data = discord_client.data.write().await;
data.insert::<SenderKey>(irc_client.sender());
data.insert::<MembersKey>(members.clone());
2022-01-15 15:33:10 -06:00
data.insert::<ChannelIdKey>(channel_id);
2022-01-16 19:03:50 -06:00
data.insert::<StringKey>(conf.channel);
2021-12-26 15:35:49 -06:00
}
let webhook = parse_webhook_url(http.clone(), conf.webhook)
2021-12-27 17:33:09 -06:00
.await
.expect("Invalid webhook URL");
2021-12-27 14:46:12 -06:00
2022-01-24 11:18:00 -06:00
irc_loop(irc_client, http, channel_id, webhook, members).await?;
2021-12-27 11:24:31 -06:00
discord_client.start().await?;
2021-12-26 15:13:09 -06:00
Ok(())
}
2022-01-16 19:03:50 -06:00
async fn send_irc_message(sender: &Sender, channel: &str, content: &str) -> anyhow::Result<()> {
sender.send_privmsg(channel, content)?;
2021-12-27 13:28:35 -06:00
Ok(())
}
2021-12-27 11:24:31 -06:00
async fn irc_loop(
mut client: IrcClient,
http: Arc<Http>,
channel_id: ChannelId,
2021-12-27 17:33:09 -06:00
webhook: Option<Webhook>,
members: Arc<Mutex<Vec<Member>>>,
2021-12-27 11:24:31 -06:00
) -> anyhow::Result<()> {
2021-12-27 17:33:09 -06:00
let mut avatar_cache: HashMap<String, Option<String>> = HashMap::new();
let mut id_cache: HashMap<String, Option<u64>> = HashMap::new();
lazy_static! {
static ref PING_NICK_1: Regex = Regex::new(r"^[\w+]+(:|,)").unwrap();
static ref PING_RE_2: Regex = Regex::new(r"@[^0-9\s]+").unwrap();
2022-01-16 19:04:15 -06:00
static ref CONTROL_CHAR_RE: Regex =
Regex::new(r"\x1f|\x02|\x12|\x0f|\x16|\x03(?:\d{1,2}(?:,\d{1,2})?)?").unwrap();
2022-01-18 09:48:08 -06:00
static ref WHITESPACE_RE: Regex = Regex::new(r"^\s").unwrap();
2022-01-23 18:37:37 -06:00
static ref CHANNEL_RE: Regex = Regex::new(r"#[A-Za-z-*]+").unwrap();
}
2021-12-27 17:33:09 -06:00
2021-12-27 11:24:31 -06:00
client.identify()?;
let mut stream = client.stream()?;
2021-12-27 17:33:09 -06:00
2022-01-23 18:37:37 -06:00
let channels = channel_id
.to_channel(&http)
.await?
.guild()
.unwrap()
.guild_id
.channels(&http)
.await?;
2021-12-27 11:24:31 -06:00
while let Some(orig_message) = stream.next().await.transpose()? {
print!("{}", orig_message);
2021-12-27 13:28:35 -06:00
if let Command::PRIVMSG(_, ref message) = orig_message.command {
2021-12-27 11:24:31 -06:00
let nickname = orig_message.source_nickname().unwrap();
let mut mentioned_1: Option<u64> = None;
2022-01-06 13:29:12 -06:00
if PING_NICK_1.is_match(message) {
if let Some(mat) = PING_NICK_1.find(message) {
2022-01-07 11:33:25 -06:00
let slice = &message[mat.start()..mat.end() - 1];
if let Some(id) = id_cache.get(slice) {
mentioned_1 = id.to_owned();
} else {
2022-01-06 13:29:12 -06:00
let mut found = false;
for member in &*members.lock().await {
2022-01-06 13:29:12 -06:00
let nick = match &member.nick {
Some(s) => s.to_owned(),
2022-01-07 11:33:25 -06:00
None => member.user.name.clone(),
2022-01-06 13:29:12 -06:00
};
2022-01-21 11:01:16 -06:00
if slice.starts_with(&nick) {
2022-01-06 13:29:12 -06:00
found = true;
let id = member.user.id.0;
mentioned_1 = Some(id);
}
2022-01-06 13:29:12 -06:00
}
2022-01-06 13:29:12 -06:00
if !found {
mentioned_1 = None;
}
}
}
2022-01-06 13:29:12 -06:00
}
2022-01-06 13:49:41 -06:00
if PING_RE_2.is_match(message) {
for mat in PING_RE_2.find_iter(message) {
2022-01-07 11:33:25 -06:00
let slice = &message[mat.start() + 1..mat.end()];
dbg!(slice);
2022-01-06 13:49:41 -06:00
if id_cache.get(slice).is_none() {
let mut found = false;
for member in &*members.lock().await {
2022-01-06 13:49:41 -06:00
let nick = match &member.nick {
Some(s) => s.to_owned(),
None => member.user.name.clone(),
};
2022-01-21 11:01:16 -06:00
if slice.starts_with(&nick) {
2022-01-06 13:49:41 -06:00
found = true;
let id = member.user.id.0;
id_cache.insert(slice.to_string(), Some(id));
2022-01-06 13:49:41 -06:00
break;
}
}
if !found {
id_cache.insert(slice.to_string(), None);
2022-01-06 13:49:41 -06:00
}
}
}
}
2021-12-27 16:41:27 -06:00
if let Some(ref webhook) = webhook {
2021-12-27 17:33:09 -06:00
if avatar_cache.get(nickname).is_none() {
let mut found = false;
for member in &*members.lock().await {
2021-12-27 17:33:09 -06:00
let nick = match &member.nick {
Some(s) => s.to_owned(),
None => member.user.name.clone(),
};
if nick == nickname {
found = true;
let avatar_url = member.user.avatar_url();
avatar_cache.insert(nickname.to_string(), avatar_url);
break;
}
}
if !found {
avatar_cache.insert(nickname.to_string(), None); // user is not in the guild
}
}
2021-12-27 17:33:09 -06:00
webhook
.execute(&http, false, |w| {
if let Some(cached) = avatar_cache.get(nickname) {
if let &Some(ref url) = cached {
w.avatar_url(url);
}
}
let mut computed = message.to_string();
let mut is_code = false;
if WHITESPACE_RE.is_match(&computed) {
computed = format!("`{}`", computed);
is_code = true;
}
2022-01-07 11:33:25 -06:00
if !is_code {
for mat in PING_RE_2.find_iter(message) {
let slice = &message[mat.start() + 1..mat.end()];
if let Some(cached) = id_cache.get(slice) {
if let &Some(id) = cached {
computed = PING_RE_2
.replace(&computed, format!("<@{}>", id))
.to_string();
}
}
}
if let Some(id) = mentioned_1 {
computed = PING_NICK_1
.replace(&computed, format!("<@{}>", id))
.to_string();
}
2022-01-23 18:07:34 -06:00
2022-01-23 18:37:37 -06:00
for mat in CHANNEL_RE.find_iter(message) {
2022-01-23 18:40:51 -06:00
let slice = &message[mat.start() + 1..mat.end()];
2022-01-23 18:37:37 -06:00
2022-01-23 18:40:51 -06:00
if let Some((id, _)) =
channels.iter().find(|(_, c)| c.name == slice)
{
computed = CHANNEL_RE
.replace(&computed, format!("<#{}>", id.0))
.to_string();
2022-01-23 18:37:37 -06:00
}
}
2022-01-18 17:20:50 -06:00
let mut has_opened_bold = false;
let mut has_opened_italic = false;
2022-01-18 17:22:06 -06:00
2022-01-18 17:20:50 -06:00
for c in computed.clone().chars() {
if c == '\x02' {
computed = computed.replace('\x02', "**");
has_opened_bold = true;
}
2022-01-18 17:22:06 -06:00
2022-01-18 17:20:50 -06:00
if c == '\x1D' {
computed = computed.replace('\x1D', "*");
has_opened_italic = true;
}
2022-01-18 17:22:06 -06:00
2022-01-18 17:20:50 -06:00
if c == '\x0F' {
if has_opened_italic {
computed = computed.replace('\x0F', "*");
has_opened_italic = false;
} else if has_opened_bold {
computed = computed.replace('\x0F', "**");
has_opened_bold = false;
}
}
}
if has_opened_italic {
computed.push_str("*");
}
if has_opened_bold {
computed.push_str("**");
}
2022-01-15 15:13:35 -06:00
computed = CONTROL_CHAR_RE.replace_all(&computed, "").to_string();
2022-01-18 09:48:08 -06:00
}
2021-12-27 17:33:09 -06:00
w.username(nickname);
w.content(computed);
2021-12-27 17:33:09 -06:00
w
})
.await?;
2021-12-27 16:41:27 -06:00
} else {
2022-01-07 11:33:25 -06:00
let mut computed = message.to_string();
2022-01-07 11:33:25 -06:00
for mat in PING_RE_2.find_iter(message) {
let slice = &message[mat.start() + 1..mat.end()];
if let Some(cached) = id_cache.get(slice) {
if let &Some(id) = cached {
computed = PING_RE_2
.replace(&computed, format!("<@{}>", id))
.to_string();
}
2022-01-07 11:33:25 -06:00
}
}
if let Some(id) = mentioned_1 {
computed = PING_NICK_1
.replace(&computed, format!("<@{}>", id))
.to_string();
}
for mat in CHANNEL_RE.find_iter(message) {
let slice = &message[mat.start() + 1..mat.end()];
if let Some((id, _)) = channels.iter().find(|(_, c)| c.name == slice) {
computed = CHANNEL_RE
.replace(&computed, format!("<#{}>", id.0))
.to_string();
}
}
2022-01-18 17:21:48 -06:00
let mut has_opened_bold = false;
2022-01-18 17:22:06 -06:00
let mut has_opened_italic = false;
2022-01-18 17:21:48 -06:00
2022-01-18 17:22:06 -06:00
for c in computed.clone().chars() {
if c == '\x02' {
computed = computed.replace('\x02', "**");
has_opened_bold = true;
}
2022-01-18 17:21:48 -06:00
2022-01-18 17:22:06 -06:00
if c == '\x1D' {
computed = computed.replace('\x1D', "*");
has_opened_italic = true;
}
if c == '\x0F' {
if has_opened_italic {
computed = computed.replace('\x0F', "*");
has_opened_italic = false;
} else if has_opened_bold {
computed = computed.replace('\x0F', "**");
has_opened_bold = false;
}
}
}
if has_opened_italic {
computed.push_str("*");
}
if has_opened_bold {
computed.push_str("**");
}
2022-01-18 17:21:48 -06:00
channel_id
.say(&http, format!("<{}> {}", nickname, computed))
.await?;
2021-12-27 16:41:27 -06:00
}
2022-01-03 15:19:53 -06:00
} else if let Command::JOIN(_, _, _) = orig_message.command {
let nickname = orig_message.source_nickname().unwrap();
channel_id
.say(&http, format!("*{}* has joined the channel", nickname))
.await?;
2022-01-03 15:20:03 -06:00
} else if let Command::PART(_, ref reason) | Command::QUIT(ref reason) =
orig_message.command
{
2022-01-03 15:19:53 -06:00
let nickname = orig_message.source_nickname().unwrap();
2022-01-03 15:20:03 -06:00
let reason = reason
.as_ref()
.unwrap_or(&String::from("Connection closed"))
.to_string();
2022-01-03 15:19:53 -06:00
channel_id
.say(&http, format!("*{}* has quit ({})", nickname, reason))
.await?;
2022-01-23 12:59:57 -06:00
} else if let Command::NICK(ref new_nick) = orig_message.command {
let old_nick = orig_message.source_nickname().unwrap();
channel_id
.say(
&http,
format!("*{}* is now known as *{}*", old_nick, new_nick),
)
.await?;
2021-12-27 11:24:31 -06:00
}
}
Ok(())
2021-12-26 16:05:53 -06:00
}
2021-12-27 14:46:12 -06:00
2021-12-27 17:33:09 -06:00
async fn parse_webhook_url(
http: Arc<Http>,
url: Option<String>,
) -> anyhow::Result<Option<Webhook>> {
2021-12-27 14:46:12 -06:00
if let Some(url) = url {
let url = url.trim_start_matches("https://discord.com/api/webhooks/");
let split = url.split("/").collect::<Vec<&str>>();
2021-12-27 16:44:34 -06:00
let id = split[0].parse::<u64>()?;
2021-12-27 14:46:12 -06:00
let token = split[1].to_string();
2021-12-27 16:44:34 -06:00
let webhook = http.get_webhook_with_token(id, &token).await?;
Ok(Some(webhook))
2021-12-27 14:46:12 -06:00
} else {
2021-12-27 16:44:34 -06:00
Ok(None)
2021-12-27 14:46:12 -06:00
}
2021-12-27 17:33:09 -06:00
}