Coder Social home page Coder Social logo

robotty / twitch-irc-rs Goto Github PK

View Code? Open in Web Editor NEW
99.0 5.0 23.0 555 KB

Twitch IRC library for the Rust programming language

Home Page: https://crates.io/crates/twitch-irc

License: MIT License

Rust 100.00%
twitch-irc-library rust-programming-language twitch irc twitch-bot twitchtv chatbot crate

twitch-irc-rs's People

Contributors

chronophylos avatar coffelius avatar demize avatar dependabot[bot] avatar dnaka91 avatar foxlisk avatar ilya-zlobintsev avatar nerixyz avatar pajlada avatar peddermaster2 avatar randers00 avatar retoon avatar teotwaki avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

twitch-irc-rs's Issues

Overview for missing sending functions

  • whisper
  • setColor
  • ban - done via #110
  • timeout - done via #110
  • deleteMessage
  • /me
  • say in reply - done via #84
  • /me in reply (?)
  • reply to privmsg with say - done via #84
  • reply to privmsg with /me (?)

And then utility functions for replying to a PRIVMSG message

#55 #84

Can't join channels anymore

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) )

Display more information in log messages

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.

Make RefreshingLoginCredentials shareable

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

Should not differentiate between empty and missing tag values

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

Reduce Allocations

Especially when parsing IRC messages, a lot of Strings 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).

[RUSTSEC-2023-0052] webpki: CPU denial of service in certificate path building

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!


Output of `cargo audit` after a standard `cargo build`:
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.

make use of Uuid crate and integer parsing values like room-id

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 ids 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 garbage collection for unecessary connections

  • library user joins a lot of channels
  • those channels are parted later
  • a lot of empty connections are now left over

same thing happens when a big burst of messages is sent once and then that same pattern of usage never occurs again later.

Add support for reply-parent tags

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.

ServerMessage not parsing correctly

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)

Add support for highlighted messages

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.

Set Capabilities

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.

Setting to (not) get back own messages reliably

Three states:

  • "Doesn't matter" - when a PRIVMSG message is sent, the connection chosen doesn't matter.
  • "Must not be joined" - only send PRIVMSG messages to connections where that channel isn't joined. This means the library user gets their own messages back as PRIVMSG, reliably. If the channel is not joined on any of the connections then the selected connection doesn't matter.
  • "Must be joined" - only send PRIVMSG messages to the connection that is joined to that channel. If the channel is not joined on any of the connections then the selected connection doesn't matter. This means potentially sending messages even if the connection would otherwise currently be too busy.

Extension for parsing undocumented message attributes

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.

Issue with example ?

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 public constructor to UserAccessToken?

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?

whispers

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?

Set wanted channels command bug

If you pass enough big hashset to set wanted channels method, only part of targeted channels will be connected, monitored via Prometheus metrics.

`TokenStorage` trait signature doesn't allow for UAT initialisation

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:

  • Upon initialisation of my TokenStorage, initialise a fake UserAccessToken with an intentionally expired date. This seems quite hackish.
  • Modify RefreshingLoginCredentials<S>::get_credentials() to first try to update the credentials prior to bailing out after receiving a LoadError. This seems suboptimal.
  • Modify the 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.

Aggressive use of `panic!()`

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:

  • I don't want libraries that I use to call 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.
  • There is a function allowing you to verify whether the channel is apparently OK or not. So why panic at all?

I'm happy to implement the change and provide a PR if you're OK with it.

Thanks

Reduce usage of unbounded channels.

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?

Improve pooling resources.

Just a few thoughts about pooling resources. In general, there are several resources to manage:

  • Connection to channels
  • Connection to twitch IRC servers as client

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.

Underlying transport failed to connect.

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.

Informative events API

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?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.