Skip to main content

mirai/
session.rs

1//! This mod provides a way to communicate with a mirai-api-http server.
2//!
3//! ## MiraiServer
4//!
5//! First, you should construct a [`MiraiServer`], it contains a [`base_url`] property which is the address to the server.
6//!
7//! ```rust
8//! use mirai::session::MiraiServer;
9//!
10//! let server = MiraiServer::new("http://localhost:8080");
11//! ```
12//!
13//! You can use [`MiraiServer::about`] function to get the server status.
14//!
15//! ## Session
16//!
17//! Second, you can use [`MiraiServer::auth`] to authorize, the auth key can be found in mirai-console output when it starts.
18//!
19//! ```rust
20//! let session = server.auth("auth_key_should_be_kept_secret");
21//! ```
22//!
23//! After authorization, you can bind your session with a bot that is logged in the server.
24//!
25//! ```rust
26//! let account = "QQ Account".parse().unwrap();
27//! session.verify(account);
28//! ```
29//!
30//! You can send and get messages now!
31//!
32//! After these, you should release the connection which your session to a bot.
33//!
34//! ```rust
35//! session.release(account);
36//! ```
37//!
38//! If not, the useless bot will continue to receive messages, this will bring **memory leak**.
39//!
40
41use reqwest::Client;
42use serde::{Deserialize, Serialize};
43
44use crate::error::{Result, ImpossibleError, assert};
45use crate::{Code, Target};
46
47
48/// # MiraiServer
49///
50/// mirai server contains server address ([base_url]).
51#[derive(Clone, Debug)]
52pub struct MiraiServer {
53    pub base_url: String
54}
55
56/// # Session
57///
58/// a session which authorized with a mirai server ([server])
59#[derive(Debug)]
60pub struct Session {
61    pub(crate) client: Client,
62    pub server: MiraiServer,
63    pub key: String,
64}
65
66#[derive(Deserialize)]
67pub(crate) struct CommonResponse {
68    pub code: Code,
69    pub msg: String,
70}
71
72#[derive(Deserialize)]
73pub struct AboutResponse {
74    pub code: Code,
75    pub data: AboutData,
76}
77
78#[derive(Deserialize)]
79pub struct AboutData {
80    pub version: String
81}
82
83impl MiraiServer {
84    pub fn new(base_url: &str) -> MiraiServer {
85        MiraiServer {
86            base_url: base_url.to_string()
87        }
88    }
89
90    pub fn url(&self, path: &str) -> String {
91        self.base_url.clone() + path
92    }
93
94    pub async fn about(&self) -> Result<AboutResponse> {
95        let resp: AboutResponse = reqwest::get(&self.url("/about"))
96            .await?
97            .json().await?;
98
99        Ok(resp)
100    }
101
102    pub async fn auth(&self, auth_key: &str) -> Result<Session> {
103        #[derive(Serialize)]
104        struct Request {
105            #[serde(rename = "authKey")]
106            auth_key: String
107        }
108
109        #[derive(Deserialize)]
110        struct Response {
111            code: Code,
112            session: Option<String>,
113        }
114
115        let client = Client::new();
116        let req = Request {
117            auth_key: auth_key.to_string()
118        };
119
120        let result: Response = client.post(&self.url("/auth"))
121            .json(&req).send().await?
122            .json().await?;
123
124        assert(result.code, "Auth")?;
125
126        Ok(Session {
127            client,
128            server: self.clone(),
129            key: result.session.ok_or(ImpossibleError("session is None".to_string()))?,
130        })
131    }
132
133    pub async fn run_command(&self, auth_key: &str, command: &str, args: &[&str]) -> Result<String> {
134        #[serde(rename_all = "camelCase")]
135        #[derive(Serialize)]
136        struct Request<'s> {
137            auth_key: &'s str,
138            name: &'s str,
139            args: &'s [&'s str],
140        }
141
142        let req = Request {
143            auth_key,
144            name: command,
145            args,
146        };
147
148        let client = Client::new();
149        let text = client.post(&self.url("/command/send"))
150            .json(&req).send().await?
151            .text().await?;
152
153        Ok(text)
154    }
155}
156
157impl Session {
158    pub fn url(&self, path: &str) -> String {
159        self.server.url(path)
160    }
161
162    pub async fn verify(&self, qq: Target) -> Result<()> {
163        #[derive(Serialize)]
164        struct Request {
165            #[serde(rename = "sessionKey")]
166            session_key: String,
167            qq: Target,
168        }
169
170        let req = Request {
171            session_key: self.key.clone(),
172            qq,
173        };
174
175        let result: CommonResponse = self.client.post(&self.url("/verify"))
176            .json(&req).send().await?
177            .json().await?;
178
179        assert(result.code, "Verify")
180    }
181
182    pub async fn release(&self, qq: Target) -> Result<()> {
183        #[derive(Serialize)]
184        struct Request {
185            #[serde(rename = "sessionKey")]
186            session_key: String,
187            qq: Target,
188        }
189
190        let req = Request {
191            session_key: self.key.clone(),
192            qq,
193        };
194
195        let resp: CommonResponse = self.client.post(&self.url("/release"))
196            .json(&req).send().await?
197            .json().await?;
198
199        assert(resp.code, "Release")
200    }
201}