clemsbot/src/app/twitch.rs

129 lines
3.9 KiB
Rust

use twitch_api2::{
TwitchClient,
types::UserId,
twitch_oauth2::UserToken,
};
use irc::client::prelude::Message;
use irc::proto::{Command, Response};
use crate::app::State;
use crate::app::config::CommandExecutor;
use crate::app::rhai_tools::ExecutorState;
use std::sync::Arc;
use tokio_tungstenite::tungstenite::Message as WsMessage;
pub struct Twitch {
pub client: TwitchClient<'static, reqwest::Client>,
pub bot_token: UserToken,
pub user_token: UserToken,
}
pub async fn handle_irc_event(state: Arc<State>, event: Message) -> anyhow::Result<()> {
let channel_name = format!("#{}", state.channel_name);
match &event.command {
Command::Response(resp, _) if *resp == Response::RPL_WELCOME => {
state.irc.send_join(&channel_name)?;
},
// FIXME: do correct checking here
Command::PRIVMSG(channel, message) if *channel == channel_name => {
on_privmsg(state, &event, &message).await?;
},
_ => {
// eprintln!("{:#?}", c);
},
}
Ok(())
}
async fn on_privmsg(state: Arc<State>, event: &Message, message: &str) -> anyhow::Result<()> {
let initiator = event.source_nickname()
.ok_or_else(|| anyhow::anyhow!("missing source"))?
.to_string();
let initiator_id = event.tags
.as_ref()
.ok_or_else(|| anyhow::anyhow!("missing tags"))?
.iter()
.find(|tag| tag.0 == "user-id")
.and_then(|tag| tag.1.clone())
.ok_or_else(|| anyhow::anyhow!("missing user id"))?;
let words: Vec<&str> = message.split(' ').collect();
let command_name = words[0];
let args = words[1..].iter()
.map(ToString::to_string)
.map(rhai::Dynamic::from)
.collect();
let command = state.config.read()
.await
.commands
.iter().find(|command| command.name == command_name || command.aliases.iter().any(|alias| alias == command_name))
.map(Clone::clone);
let command = match command {
Some(c) => c,
None => return Ok(())
};
match &command.executor {
CommandExecutor::Text(t) => { state.irc_queue.send(t.to_string()).ok(); },
CommandExecutor::Rhai(t) => {
let fn_state = ExecutorState {
twitch: Arc::clone(&state.twitch),
initiator_id: UserId::new(initiator_id),
initiator,
args,
runtime: state.runtime.clone(),
};
crate::app::run_rhai(Arc::clone(&state), t, fn_state);
},
}
Ok(())
}
use twitch_api2::pubsub::{
Response as PubSubResponse,
TopicData,
channel_points::ChannelPointsChannelV1Reply,
};
pub async fn handle_pubsub(state: Arc<State>, event: WsMessage) -> anyhow::Result<()> {
let json = match event {
WsMessage::Text(json) => json,
_ => return Ok(()),
};
let response = twitch_api2::pubsub::Response::parse(&json)?;
let reply = match response {
PubSubResponse::Message { data: TopicData::ChannelPointsChannelV1 { reply, .. } } => reply,
_ => return Ok(()),
};
let redemption = match *reply {
ChannelPointsChannelV1Reply::RewardRedeemed { redemption, .. } => redemption,
_ => return Ok(()),
};
let action = match state.config.read().await.redemptions.iter().find(|re| re.twitch_id == redemption.reward.id).map(Clone::clone) {
Some(a) => a,
None => return Ok(()),
};
let args = redemption.user_input
.map(|input| input.split(' ').map(ToOwned::to_owned).map(rhai::Dynamic::from).collect())
.unwrap_or_default();
let fn_state = ExecutorState {
twitch: Arc::clone(&state.twitch),
initiator: redemption.user.login.to_string(),
initiator_id: redemption.user.id,
args,
runtime: state.runtime.clone(),
};
crate::app::run_rhai(Arc::clone(&state), &action.rhai, fn_state);
Ok(())
}