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
#![feature(type_alias_impl_trait)]

use std::sync::atomic::{AtomicI32, AtomicI64, AtomicU16, Ordering};

use bytes::Bytes;
use rand::Rng;

pub use error::{RQError, RQResult};
use protocol::device::Device;
use protocol::oicq;
use protocol::transport::Transport;
use protocol::version::Version;

pub use crate::token::Token;

pub mod binary;
pub mod command;
pub mod common;
pub mod crypto;
pub mod error;
pub mod hex;
pub mod highway;
pub mod jce;
pub mod msg;
pub mod pb;
pub mod protocol;
pub mod structs;
pub mod token;
mod utils;
pub mod wtlogin;

// build_packet: param -> bytes
// decode_packet: bytes -> struct
// this should be wrapped in a rwlock (readonly after login)
// TODO: build library for other language
// no async and await
pub struct Engine {
    pub uin: AtomicI64,
    pub transport: Transport,
    pub seq_id: AtomicU16,
    pub request_packet_request_id: AtomicI32,
    pub group_seq: AtomicI32,
    pub friend_seq: AtomicI32,
    pub group_data_trans_seq: AtomicI32,
    pub highway_apply_up_seq: AtomicI32,
}

impl Engine {
    pub fn new(device: Device, version: Version) -> Self {
        Self {
            uin: AtomicI64::new(0),
            transport: Transport::new(device, version),
            seq_id: AtomicU16::new(0x3635),
            request_packet_request_id: AtomicI32::new(1921334513),
            group_seq: AtomicI32::new(rand::thread_rng().gen_range(0..20000)),
            friend_seq: AtomicI32::new(rand::thread_rng().gen_range(0..20000)),
            group_data_trans_seq: AtomicI32::new(rand::thread_rng().gen_range(0..20000)),
            highway_apply_up_seq: AtomicI32::new(rand::thread_rng().gen_range(0..20000)),
        }
    }

    pub fn uin(&self) -> i64 {
        self.uin.load(Ordering::Relaxed)
    }

    pub fn next_seq(&self) -> u16 {
        self.seq_id.fetch_add(1, Ordering::Relaxed)
    }

    pub fn next_packet_seq(&self) -> i32 {
        self.request_packet_request_id
            .fetch_add(2, Ordering::Relaxed)
    }

    pub fn next_group_seq(&self) -> i32 {
        self.group_seq.fetch_add(2, Ordering::Relaxed)
    }

    pub fn next_friend_seq(&self) -> i32 {
        self.friend_seq.fetch_add(2, Ordering::Relaxed)
    }

    pub fn next_group_data_trans_seq(&self) -> i32 {
        self.group_data_trans_seq.fetch_add(2, Ordering::Relaxed)
    }

    pub fn next_highway_apply_seq(&self) -> i32 {
        self.highway_apply_up_seq.fetch_add(2, Ordering::Relaxed)
    }

    pub fn gen_token(&self) -> Token {
        Token {
            uin: self.uin(),
            d2: self.transport.sig.d2.to_vec(),
            d2key: self.transport.sig.d2key.to_vec(),
            tgt: self.transport.sig.tgt.to_vec(),
            srm_token: self.transport.sig.srm_token.to_vec(),
            t133: self.transport.sig.t133.to_vec(),
            encrypted_a1: self.transport.sig.encrypted_a1.to_vec(),
            out_packet_session_id: self.transport.sig.out_packet_session_id.to_vec(),
            tgtgt_key: self.transport.sig.tgtgt_key.to_vec(),
            wt_session_ticket_key: self.transport.oicq_codec.wt_session_ticket_key.to_vec(),
        }
    }

    pub fn load_token(&mut self, token: Token) {
        self.uin.store(token.uin, Ordering::Relaxed);
        self.transport.sig.d2 = Bytes::from(token.d2);
        self.transport.sig.d2key = Bytes::from(token.d2key);
        self.transport.sig.tgt = Bytes::from(token.tgt);
        self.transport.sig.srm_token = Bytes::from(token.srm_token);
        self.transport.sig.t133 = Bytes::from(token.t133);
        self.transport.sig.encrypted_a1 = Bytes::from(token.encrypted_a1);
        self.transport.sig.out_packet_session_id = Bytes::from(token.out_packet_session_id);
        self.transport.sig.tgtgt_key = Bytes::from(token.tgtgt_key);
        self.transport.oicq_codec.wt_session_ticket_key = Bytes::from(token.wt_session_ticket_key);
    }
}