forked from museun/twitchchat
-
Notifications
You must be signed in to change notification settings - Fork 0
/
tokio_demo.rs
171 lines (143 loc) · 5.16 KB
/
tokio_demo.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
// NOTE: this demo requires `--features="tokio/full tokio-util"`.
use anyhow::Context;
use twitchchat::{
commands, connector, messages,
runner::{AsyncRunner, Status},
UserConfig,
};
fn get_env_var(key: &str) -> anyhow::Result<String> {
std::env::var(key).with_context(|| format!("please set `{}`", key))
}
fn get_user_config() -> anyhow::Result<twitchchat::UserConfig> {
let name = get_env_var("TWITCH_NAME")?;
let token = get_env_var("TWITCH_TOKEN")?;
// you need a `UserConfig` to connect to Twitch
let config = UserConfig::builder()
// the name of the associated twitch account
.name(name)
// and the provided OAuth token
.token(token)
// and enable all of the advanced message signaling from Twitch
.enable_all_capabilities()
.build()?;
Ok(config)
}
fn channels_to_join() -> anyhow::Result<Vec<String>> {
let channels = get_env_var("TWITCH_CHANNEL")?
.split(',')
.map(ToString::to_string)
.collect();
Ok(channels)
}
async fn connect(user_config: &UserConfig, channels: &[String]) -> anyhow::Result<AsyncRunner> {
// create a connector using ``tokio``, this connects to Twitch.
// you can provide a different address with `custom`
let connector = connector::tokio::Connector::twitch();
println!("we're connecting!");
// create a new runner. this is a provided async 'main loop'
// this method will block until you're ready
let mut runner = AsyncRunner::connect(connector, user_config).await?;
println!("..and we're connected");
// and the identity Twitch gave you
println!("our identity: {:#?}", runner.identity);
for channel in channels {
// the runner itself has 'blocking' join/part to ensure you join/leave a channel.
// these two methods return whether the connection was closed early.
// we'll ignore it for this demo
println!("attempting to join '{}'", channel);
let _ = runner.join(&channel).await?;
println!("joined '{}'!", channel);
}
Ok(runner)
}
async fn handle_message(msg: messages::Commands<'_>) {
use messages::Commands::*;
// All sorts of messages
match msg {
// This is the one users send to channels
Privmsg(msg) => println!("[{}] {}: {}", msg.channel(), msg.name(), msg.data()),
// This one is special, if twitch adds any new message
// types, this will catch it until future releases of
// this crate add them.
Raw(_) => {}
// These happen when you initially connect
IrcReady(_) => {}
Ready(_) => {}
Cap(_) => {}
// and a bunch of other messages you may be interested in
ClearChat(_) => {}
ClearMsg(_) => {}
GlobalUserState(_) => {}
HostTarget(_) => {}
Join(_) => {}
Notice(_) => {}
Part(_) => {}
Ping(_) => {}
Pong(_) => {}
Reconnect(_) => {}
RoomState(_) => {}
UserNotice(_) => {}
UserState(_) => {}
Whisper(_) => {}
_ => {}
}
}
async fn main_loop(mut runner: AsyncRunner) -> anyhow::Result<()> {
loop {
match runner.next_message().await? {
// this is the parsed message -- across all channels (and notifications from Twitch)
Status::Message(msg) => {
handle_message(msg).await;
}
// you signaled a quit
Status::Quit => {
println!("we signaled we wanted to quit");
break;
}
// the connection closed normally
Status::Eof => {
println!("we got a 'normal' eof");
break;
}
}
}
Ok(())
}
#[tokio::main]
async fn main() -> anyhow::Result<()> {
// create a user configuration
let user_config = get_user_config()?;
// get some channels to join from the environment
let channels = channels_to_join()?;
// connect and join the provided channels
let runner = connect(&user_config, &channels).await?;
// you can get a handle to shutdown the runner
let quit_handle = runner.quit_handle();
// you can get a clonable writer
let mut writer = runner.writer();
// spawn something off in the background that'll exit in 10 seconds
tokio::spawn({
let mut writer = writer.clone();
let channels = channels.clone();
async move {
println!("in 10 seconds we'll exit");
tokio::time::delay_for(std::time::Duration::from_secs(10)).await;
// send one final message to all channels
for channel in channels {
let cmd = commands::privmsg(&channel, "goodbye, world");
writer.encode(cmd).await.unwrap();
}
println!("sending quit signal");
quit_handle.notify().await;
}
});
// you can encode all sorts of 'commands'
for channel in &channels {
writer
.encode(commands::privmsg(channel, "hello world!"))
.await?;
}
println!("starting main loop");
// your 'main loop'. you'll just call next_message() until you're done
main_loop(runner).await
}