robotty / twitch-irc-rs Goto Github PK
View Code? Open in Web Editor NEWTwitch IRC library for the Rust programming language
Home Page: https://crates.io/crates/twitch-irc
License: MIT License
Twitch IRC library for the Rust programming language
Home Page: https://crates.io/crates/twitch-irc
License: MIT License
In an effort to group all actions under the Twitch API and in the process allow further improvements to Twitch Chat, we are deprecating chat command usage through IRC as of today and these commands will no longer function via an IRC connection on or about February 18, 2023.
https://discuss.dev.twitch.tv/t/deprecation-of-chat-commands-through-irc/40486
This means I'll be removing the built-in functions to execute chat commands through IRC.
I can't join channels anymore.
I tried the example from the readme, but it doesn't work either.
client.join
is returning Ok(())
, but client.get_channel_status
returns (true, false)
and the incoming_messages.recv().await
is blocking indefinitely. (try_recv
returns Ok(Empty)
)
This library currently interprets the ID range to refer to "Unicode Scalar Values" (-> https://doc.rust-lang.org/stable/std/primitive.str.html#method.chars)
However, it has recently been revealed that these actually refer to bytes in an UTF-8 string, which has previously caused issues and has made me implement a soft fallback/workaround. See twitchdev/issues#104 (comment)
Workaround was added here: 6195c31
Whenever a connection is closed an opened again it is not easy to figure out which client had its connection closed since the log output does not give any information about the bot name. This is not a problem if you use a single client, but pretty annoying as soon as you use multiple clients in one program.
This also applies to other log messages. It would be nice if it is possible to figure out which client caused an error.
Maybe tracing could help with providing more information about what is happening.
Should downgrade to tokio 0.2 again and yank the existing release.
See also #206
This would prove useful for if one was to run multiple clients with the same login credentials, or most importantly if users want to use this library's authentication boilerplate to power things like API calls in their application too. They could clone the RefreshingLoginCredentials
before moving them into the config, which would then enable usages of get_credentials()
in other application code.
Paging @ilyazzz since you brought this to my attention in Twitch chat
Implementations MUST interpret empty tag values (e.g.
foo=
) as equivalent to missing tag values (e.g.foo
). Specifications MUST NOT differentiate meaning between tags with empty and missing values. Implementations MAY normalise tag values by converting the empty form to the missing form, but MUST NOT convert values from missing to empty, to prevent size limit issues.
https://ircv3.net/specs/extensions/message-tags
The code currently differentiates between the two
Especially when parsing IRC messages, a lot of String
s are created that are really just substrings of the original message, so a &str
would be more efficient. This would make the message struct tied to a lifetime (of the backing String
) making it hard to move the struct around. However, there are crates out there that would make this simpler, like yoke
.
I think it's worth at least discussing this.
Also see this article on yoke
: Not a Yoking Matter (Zero-Copy #1).
It appears this applies likely because of webpki-roots = { version = "0.22", optional = true }
at L#48 in Cargo.toml
Crate: webpki
Version: 0.22.0
Title: webpki: CPU denial of service in certificate path building
Date: 2023-08-22
ID: RUSTSEC-2023-0052
URL: https://rustsec.org/advisories/RUSTSEC-2023-0052
Severity: 7.5 (high)
Solution: No fixed upgrade is available!
webpki 0.22.0
├── webpki-roots 0.22.6
│ ├── twitch-irc 5.0.0
│ ├── reqwest 0.11.14
│ │ └── twitch-irc 5.0.0
│ └── async-tungstenite 0.17.2
│ └── twitch-irc 5.0.0
├── tungstenite 0.17.3
│ └── async-tungstenite 0.17.2
├── tokio-rustls 0.23.4
│ ├── twitch-irc 5.0.0
│ ├── reqwest 0.11.14
│ ├── hyper-rustls 0.23.2
│ │ └── reqwest 0.11.14
│ └── async-tungstenite 0.17.2
└── rustls 0.20.8
├── tungstenite 0.17.3
├── tokio-rustls 0.23.4
├── reqwest 0.11.14
└── hyper-rustls 0.23.2
This was fixed in denoland/deno#20285 by updating to name webpki-roots
to 0.25.2
which removes the inherit dependency on webpki
. They also had to bump their reqwest
version to 0.11.20
(which would likely get picked up with a simple cargo update
)
It looks like an update would fix things. I don't know if that should/can get caught up in #189 or if that should land separately.
From what i understand, things like room-id and user-id are all integers wrapped around strings, i propose to parse these strings to some primitive integer type.
twitch message id
s are 128bit UUIDs, it would be nice to use the uuid
crate and parse these into the Uuid
type, as they have some nice features and still allow the end user to return them as a String in the future. It is mainly useful for when you want to store something in a database with for example sqlx
and postgres
because they bind the Uuid
type to uuid columns in the database.
If this is something you guys agree would be useful it should probably be implemented before #187 because it also involves uuids
Edits: fixing many small mistakes
Add example
same thing happens when a big burst of messages is sent once and then that same pattern of usage never occurs again later.
https://crates.io/crates/async-std as an alternative to tokio
I noticed there wasn't support for reply-parent tags, these tags contain information about a message a user is replying to in chat, the tags are optional and not present when the message is not in reply to another message.
see: https://dev.twitch.tv/docs/irc/tags/#twitch-irc-tags
I suggest something like a ReplyParent
struct type that contains the five reply-parent related tags, msg-id
, user-login
, user-login
, display-name
, msg-body
if i have time i'll try to fork and implement it but i'm not terribly familiar with developing crates or libraries, but will give it a shot.
Hello !
I'd like to have an event when user claim reward with his channel points. Does the library support it?
https://help.twitch.tv/s/article/channel-points-guide?language=en_US
Regards
Waiting for:
I guess this type of message is new and not supported yet. It's a message that I saw today shortly after being raided (the raid message itself didn't cause any error):
2021-02-27 17:01:34,327 ERROR [twitch_irc::connection::event_loop] Failed to parse incoming message as ServerMessage (emitting as generic instead): Could not parse IRC message :[email protected] PRIVMSG stuck_overflow :laempie is now hosting you for up to 6 viewers. as ServerMessage: Malformed channel parameter (# must be present + something after it)
If you send a /mods
, /vips
or any other command via PRIVMSG
it is impossible to parse the response sent by twitch.
It gets parsed as a ServerMessage::Generic(HiddenIRCMessage)
which is hidden and not publicly accessible, but required to get the result of a command.
Currently it seems highlighted messages just get passed through to privmsg without any field for being highlighted. In the source you can see there is a msg-id
field in tags
filled with highlighted-message
.
I couldn't find anything in the docs on Twitch's site about this but imo this should be treated similar to /me
messages and get it's own bool field that defaults to false
..?
Similarly it would probably be useful to include the tag for first-msg
and returning-chatter
, although I'm not sure how stable the latter is, it seems that is still experimental.
Literally, how to check that's connect to channel is successful and messages comes in?
I'd like to set irc capabilites without having to send the CAP message when connecting to the irc. Specifically I want the twitch.tv/membership
capability.
Three states:
Some Twitch features such as channel point redeems are not documented but would be nice if we could parse them. I'm thinking the library could include an extension trait with functions to parse these details, e.g. ChannelPointRedemptionInformation
from a PrivmsgMessage
.
It would also probably not be a bad idea to expose IRCMessageParseExt
to allow third parties to parse details on their own, if needed.
Update the docs tokio's recv() instead of next(). https://tokio-rs.github.io/tokio/doc/tokio/sync/mpsc/struct.UnboundedReceiver.html
I try to compile code from example (in my own code) but :
error[E0382]: use of moved value: `incoming_messages`
--> src/twitch.rs:188:47
|
140 | let (mut incoming_messages, client) =
| --------------------- move occurs because `incoming_messages` has type `UnboundedReceiver<ServerMessage>`, which does not implement the `Copy` trait
...
143 | let join_handle = tokio::spawn(async move {
| _______________________________________________-
144 | | while let Some(message) = incoming_messages.recv().await {
| | ----------------- variable moved due to use in generator
145 | | tracing::info!("Received message: {:?}", message);
146 | | }
147 | | });
| |_____- value moved here
...
188 | let join_handle = tokio::spawn(async move {
| _______________________________________________^
189 | | while let Some(message) = incoming_messages.recv().await {
| | ----------------- use occurs due to use in generator
190 | | tracing::info!("Received message: {:?}", message);
191 | | }
192 | | });
| |_____^ value used here after move
For more information about this error, try `rustc --explain E0382`.
Add a mechanism to enable automatic throttling in case the message rate exceeds the limits imposed by Twitch. Should support known and verified bots.
New to Rust so forgive me if I'm holding it wrong.
I'm implementing a twitch bot for fun and I implemented a basic flow using RefreshingLoginCredentials
. When obtaining a token for the first time from Twitch API, I have to find a way to pass it to my CustomTokenStorage
. I couldn't find a way to construct a UserAccessToken
from within the code:
#[derive(Debug, Serialize, Deserialize)]
pub struct MyUserAccessToken {
access_token: String,
refresh_token: String,
created_at: DateTime<Utc>,
expires_at: Option<DateTime<Utc>>,
}
#[tokio::main]
pub async fn main() {
// [...]
let mut storage = CustomTokenStorage {
token_checkpoint_file: config.twitch.token_filepath,
};
if !args.first_token_file.is_empty() {
let first_token = fs::read_to_string(args.first_token_file).unwrap();
let first_token: FirstToken = serde_json::from_str(&first_token).unwrap();
let created_at = Utc::now();
let expires_at = created_at + Duration::seconds(first_token.expires_in);
let user_access_token = MyUserAccessToken {
access_token: first_token.access_token,
refresh_token: first_token.refresh_token,
created_at: created_at,
expires_at: Some(expires_at),
};
let serialized = serde_json::to_string(&user_access_token).unwrap();
let user_access_token: UserAccessToken = serde_json::from_str(&serialized).unwrap();
storage.update_token(&user_access_token).await.unwrap();
}
// [...]
}
https://github.com/stuck-overflow/ferris-bot/blob/main/src/main.rs#L239
It would be much simpler if I could construct the UserAccessToken
with a public constructor. Any reason why it is private?
I know the tokio runtime needs to support WASM which is only just starting to get implemented. Are there any other things that need to be addressed?
I'm building a little bot and i want it also to respond to whispers.
i cant seem to find a function to call for sending whispers.
am i missing something?
Currently only TLS-secured IRC and WSS (secure websocket) are supported.
If you pass enough big hashset to set wanted channels method, only part of targeted channels will be connected, monitored via Prometheus metrics.
Hi,
The current version of TokenStorage
has the following signature for load_token
:
async fn load_token(&mut self) -> Result<UserAccessToken, Self::LoadError>;
I expected that upon failure (e.g.: FileNotFound
), RefreshingLoginCredentials
would fetch credentials from twitch and then attempt to store them in the TokenStorage
. However, this appears to not be the case:
let mut current_token = token_storage
.load_token()
.await
.map_err(RefreshingLoginError::LoadError)?;
I see three options:
TokenStorage
, initialise a fake UserAccessToken
with an intentionally expired date. This seems quite hackish.RefreshingLoginCredentials<S>::get_credentials()
to first try to update the credentials prior to bailing out after receiving a LoadError
. This seems suboptimal.TokenStorage
trait to return Result<Option<UserAccessToken>, Self::LoadError>
to indicate to RefreshingLoginCredentials<S>::get_credentials()
that the credential has never been initialised. get_credentials
would simply try to update_token
prior to bailing out.Again, I'm happy to make the necessary code changes and provide a PR for this.
There is, obviously, the ever glaring option that I'm an idiot who misunderstood the API. In that case, please feel free to admonish me and correct the error of my ways.
Hi,
Thanks a lot for this library. Very nice!
From the documentation for TwitchIRCClient::join
:
This method panics if a channel login of invalid format is passed to it. (As determined
by [crate::validate::validate_login].) If you are dealing with unsanitized
user input, you must manually call this validate function before calling this function,
in order to avoid panicking your program.
Would you be open to changing the signature of these functions to return something like a Result<(), ValidationError>
instead of panicking? A few reasons for my request:
panic!()
for trivial reasons.panic!()
should only be used when something goes really wrong. Attempting to join an ill-formed channel should not be that.I'm happy to implement the change and provide a PR if you're OK with it.
Thanks
Let's use more smart string types, such as FastStr provided via fast_str crate.
Lots of places use unbounded channels. Need channels with backpressure, or some sort of "try send, if unsuccessfull kill this connection/client" sort of thing?
Just a few thoughts about pooling resources. In general, there are several resources to manage:
I suggest to use crate like bb8
as pool manager. It's may simplify code and help with connection GC. Also, might worth to pool even main client connections, to allow scale bigger than 1k connections per client. Welcome to discuss.
ERROR client_loop: twitch_irc::client::event_loop: Pool connection 2103 has failed due to error (removing it): Underlying transport failed to connect: error:10080002:BIO routines:file_ctrl:system lib:../crypto/bio/bss_file.c:300:, error:05880002:x509 certificate routines:X509_load_cert_file_ex:system lib:../crypto/x509/by_file.c:100:, error:16000069:STORE routines:ossl_store_get0_loader_int:unregistered scheme:../crypto/store/store_register.c:237:scheme=file, error:80000018:system library:file_open_dir:reason(2):../providers/implementations/storemgmt/file_store.c:183:Calling OPENSSL_DIR_read("/usr/lib/ssl/certs"), error:80000018:system library:file_ctrl:reason(2):../crypto/bio/bss_file.c:297:calling fopen(/usr/lib/ssl/certs/f387163d.0, r), error:10080002:BIO routines:file_ctrl:system lib:../crypto/bio/bss_file.c:300:, error:05880002:x509 certificate routines:X509_load_cert_file_ex:system lib:../crypto/x509/by_file.c:100:, error:16000069:STORE routines:ossl_store_get0_loader_int:unregistered scheme:../crypto/store/store_register.c:237:scheme=file, error:80000018:system library:file_open_dir:reason(2):../providers/implementations/storemgmt/file_store.c:183:Calling OPENSSL_DIR_read("/usr/lib/ssl/certs"), error:80000018:system library:file_ctrl:reason(2):../crypto/bio/bss_file.c:297:calling fopen(/usr/lib/ssl/certs/f387163d.0, r), error:10080002:BIO routines:file_ctrl:system lib:../crypto/bio/bss_file.c:300:, error:05880002:x509 certificate routines:X509_load_cert_file_ex:system lib:../crypto/x509/by_file.c:100:, error:16000069:STORE routines:ossl_store_get0_loader_int:unregistered scheme:../crypto/store/store_register.c:237:scheme=file, error:80000018:system library:file_open_dir:reason(2):../providers/implementations/storemgmt/file_store.c:183:Calling OPENSSL_DIR_read("/usr/lib/ssl/certs"), error:0A000086:SSL routines:tls_post_process_server_certificate:certificate verify failed:../ssl/statem/statem_clnt.c:1883: (unable to get local issuer certificate)
After that error reconnections goes cycle, with same result.
Sometimes I got an errors like this.
ERROR client_loop: twitch_irc::client::event_loop: Pool connection 68 has failed due to error (removing it): Did not receive a PONG back after sending PING
How to catch that errors in code? How to treat that?
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.