use crate::error::Result;
use crate::http::HttpClient;
use crate::models::{
announce::{Announce, AnnouncesType, RecommendChannel},
api::{AudioAction, BotInfo, GatewayResponse, MessageResponse},
channel::{Channel, ChannelPermissions, ChannelSubType, ChannelType},
emoji::EmojiType,
guild::{Guild, GuildRole, GuildRoles, Member},
message::{
Ark, C2CMessageParams, DirectMessageParams, Embed, GroupMessageParams, Keyboard,
KeyboardPayload, MarkdownPayload, Media, Message, MessageParams, Reference,
},
permission::{APIPermission, APIPermissionDemand, APIPermissionDemandIdentify},
schedule::{RemindType, Schedule},
};
use crate::reaction::ReactionUsers;
use crate::token::Token;
use base64::Engine;
use serde_json::{Value, json};
use std::collections::HashMap;
use tracing::debug;
#[derive(Clone)]
pub struct BotApi {
http: HttpClient,
}
impl BotApi {
pub fn new(http: HttpClient) -> Self {
Self { http }
}
pub async fn get_bot_info(&self, token: &Token) -> Result<BotInfo> {
debug!("Getting bot info");
let response = self.http.get(token, "/users/@me", None::<&()>).await?;
Ok(serde_json::from_value(response)?)
}
pub async fn get_gateway(&self, token: &Token) -> Result<GatewayResponse> {
debug!("Getting gateway URL");
let response = self.http.get(token, "/gateway/bot", None::<&()>).await?;
Ok(serde_json::from_value(response)?)
}
pub async fn get_guild(&self, token: &Token, guild_id: &str) -> Result<Guild> {
debug!("Getting guild {}", guild_id);
let path = format!("/guilds/{guild_id}");
let response = self.http.get(token, &path, None::<&()>).await?;
Ok(serde_json::from_value(response)?)
}
pub async fn get_guilds(
&self,
token: &Token,
guild_id: Option<&str>,
limit: Option<u32>,
desc: Option<bool>,
) -> Result<Vec<Guild>> {
debug!("Getting guilds");
let mut params = HashMap::new();
if let Some(limit) = limit {
params.insert("limit", limit.to_string());
}
if let Some(guild_id) = guild_id {
if desc.unwrap_or(false) {
params.insert("before", guild_id.to_string());
} else {
params.insert("after", guild_id.to_string());
}
}
let response = self
.http
.get(
token,
"/users/@me/guilds",
if params.is_empty() {
None
} else {
Some(¶ms)
},
)
.await?;
Ok(serde_json::from_value(response)?)
}
pub async fn get_guild_roles(&self, token: &Token, guild_id: &str) -> Result<GuildRoles> {
debug!("Getting guild roles for {}", guild_id);
let path = format!("/guilds/{guild_id}/roles");
let response = self.http.get(token, &path, None::<&()>).await?;
Ok(serde_json::from_value(response)?)
}
pub async fn create_guild_role(
&self,
token: &Token,
guild_id: &str,
name: Option<&str>,
color: Option<u32>,
hoist: Option<bool>,
) -> Result<GuildRole> {
debug!("Creating guild role in {}", guild_id);
let mut body = HashMap::new();
if let Some(name) = name {
body.insert("name", json!(name));
}
if let Some(color) = color {
body.insert("color", json!(color));
}
if let Some(hoist) = hoist {
body.insert("hoist", json!(if hoist { 1 } else { 0 }));
}
let path = format!("/guilds/{guild_id}/roles");
let response = self
.http
.post(token, &path, None::<&()>, Some(&body))
.await?;
Ok(serde_json::from_value(response)?)
}
pub async fn update_guild_role(
&self,
token: &Token,
guild_id: &str,
role_id: &str,
name: Option<&str>,
color: Option<u32>,
hoist: Option<bool>,
) -> Result<GuildRole> {
debug!("Updating guild role {} in {}", role_id, guild_id);
let mut body = HashMap::new();
if let Some(name) = name {
body.insert("name", json!(name));
}
if let Some(color) = color {
body.insert("color", json!(color));
}
if let Some(hoist) = hoist {
body.insert("hoist", json!(if hoist { 1 } else { 0 }));
}
let path = format!("/guilds/{guild_id}/roles/{role_id}");
let response = self
.http
.put(token, &path, None::<&()>, Some(&body))
.await?;
Ok(serde_json::from_value(response)?)
}
pub async fn delete_guild_role(
&self,
token: &Token,
guild_id: &str,
role_id: &str,
) -> Result<()> {
debug!("Deleting guild role {} in {}", role_id, guild_id);
let path = format!("/guilds/{guild_id}/roles/{role_id}");
self.http.delete(token, &path, None::<&()>).await?;
Ok(())
}
pub async fn create_guild_role_member(
&self,
token: &Token,
guild_id: &str,
role_id: &str,
user_id: &str,
channel_id: Option<&str>,
) -> Result<()> {
debug!(
"Adding user {} to role {} in guild {}",
user_id, role_id, guild_id
);
let body = if let Some(channel_id) = channel_id {
json!({ "channel": { "id": channel_id } })
} else {
json!({ "channel": { "id": null } })
};
let path = format!("/guilds/{guild_id}/members/{user_id}/roles/{role_id}");
self.http
.put(token, &path, None::<&()>, Some(&body))
.await?;
Ok(())
}
pub async fn delete_guild_role_member(
&self,
token: &Token,
guild_id: &str,
role_id: &str,
user_id: &str,
channel_id: Option<&str>,
) -> Result<()> {
debug!(
"Removing user {} from role {} in guild {}",
user_id, role_id, guild_id
);
let body = if let Some(channel_id) = channel_id {
json!({ "channel": { "id": channel_id } })
} else {
json!({ "channel": { "id": null } })
};
let path = format!("/guilds/{guild_id}/members/{user_id}/roles/{role_id}");
self.http.delete(token, &path, Some(&body)).await?;
Ok(())
}
pub async fn get_guild_member(
&self,
token: &Token,
guild_id: &str,
user_id: &str,
) -> Result<Member> {
debug!("Getting guild member {} in {}", user_id, guild_id);
let path = format!("/guilds/{guild_id}/members/{user_id}");
let response = self.http.get(token, &path, None::<&()>).await?;
Ok(serde_json::from_value(response)?)
}
pub async fn get_guild_members(
&self,
token: &Token,
guild_id: &str,
after: Option<&str>,
limit: Option<u32>,
) -> Result<Vec<Member>> {
debug!("Getting guild members for {}", guild_id);
let mut params = HashMap::new();
params.insert("after", after.unwrap_or("0").to_string());
params.insert("limit", limit.unwrap_or(1).to_string());
let path = format!("/guilds/{guild_id}/members");
let response = self.http.get(token, &path, Some(¶ms)).await?;
Ok(serde_json::from_value(response)?)
}
pub async fn delete_member(
&self,
token: &Token,
guild_id: &str,
user_id: &str,
add_blacklist: Option<bool>,
delete_history_msg_days: Option<i32>,
) -> Result<()> {
debug!("Deleting member {} from guild {}", user_id, guild_id);
let delete_days = match delete_history_msg_days.unwrap_or(0) {
3 | 7 | 15 | 30 | -1 => delete_history_msg_days.unwrap_or(0),
_ => 0,
};
let body = json!({
"add_blacklist": add_blacklist.unwrap_or(false),
"delete_history_msg_days": delete_days
});
let path = format!("/guilds/{guild_id}/members/{user_id}");
self.http.delete(token, &path, Some(&body)).await?;
Ok(())
}
pub async fn get_channel(&self, token: &Token, channel_id: &str) -> Result<Channel> {
debug!("Getting channel {}", channel_id);
let path = format!("/channels/{channel_id}");
let response = self.http.get(token, &path, None::<&()>).await?;
Ok(serde_json::from_value(response)?)
}
pub async fn get_channels(&self, token: &Token, guild_id: &str) -> Result<Vec<Channel>> {
debug!("Getting channels for guild {}", guild_id);
let path = format!("/guilds/{guild_id}/channels");
let response = self.http.get(token, &path, None::<&()>).await?;
Ok(serde_json::from_value(response)?)
}
pub async fn create_channel(
&self,
token: &Token,
guild_id: &str,
name: &str,
channel_type: ChannelType,
sub_type: ChannelSubType,
position: Option<u32>,
parent_id: Option<&str>,
private_type: Option<u32>,
private_user_ids: Option<Vec<String>>,
speak_permission: Option<u32>,
application_id: Option<&str>,
) -> Result<Channel> {
debug!("Creating channel {} in guild {}", name, guild_id);
let mut body = json!({
"name": name,
"type": u32::from(channel_type),
"subtype": u32::from(sub_type)
});
if let Some(pos) = position {
body["position"] = json!(pos);
}
if let Some(parent) = parent_id {
body["parent_id"] = json!(parent);
}
if let Some(private) = private_type {
body["private_type"] = json!(private);
}
if let Some(users) = private_user_ids {
body["private_user_ids"] = json!(users);
}
if let Some(speak) = speak_permission {
body["speak_permission"] = json!(speak);
}
if let Some(app) = application_id {
body["application_id"] = json!(app);
}
let path = format!("/guilds/{guild_id}/channels");
let response = self
.http
.post(token, &path, None::<&()>, Some(&body))
.await?;
Ok(serde_json::from_value(response)?)
}
pub async fn update_channel(
&self,
token: &Token,
channel_id: &str,
name: Option<&str>,
position: Option<u32>,
parent_id: Option<&str>,
private_type: Option<u32>,
speak_permission: Option<u32>,
) -> Result<Channel> {
debug!("Updating channel {}", channel_id);
let mut body = json!({});
if let Some(name) = name {
body["name"] = json!(name);
}
if let Some(pos) = position {
body["position"] = json!(pos);
}
if let Some(parent) = parent_id {
body["parent_id"] = json!(parent);
}
if let Some(private) = private_type {
body["private_type"] = json!(private);
}
if let Some(speak) = speak_permission {
body["speak_permission"] = json!(speak);
}
let path = format!("/channels/{channel_id}");
let response = self
.http
.put(token, &path, None::<&()>, Some(&body))
.await?;
Ok(serde_json::from_value(response)?)
}
pub async fn delete_channel(&self, token: &Token, channel_id: &str) -> Result<Channel> {
debug!("Deleting channel {}", channel_id);
let path = format!("/channels/{channel_id}");
let response = self.http.delete(token, &path, None::<&()>).await?;
Ok(serde_json::from_value(response)?)
}
pub async fn get_message(
&self,
token: &Token,
channel_id: &str,
message_id: &str,
) -> Result<Message> {
debug!("Getting message {} in channel {}", message_id, channel_id);
let path = format!("/channels/{channel_id}/messages/{message_id}");
let response = self.http.get(token, &path, None::<&()>).await?;
Ok(serde_json::from_value(response)?)
}
pub async fn post_message_with_params(
&self,
token: &Token,
channel_id: &str,
params: MessageParams,
) -> Result<MessageResponse> {
debug!("Sending message to channel {}", channel_id);
let body = serde_json::to_value(¶ms)?;
let path = format!("/channels/{channel_id}/messages");
let response = self
.http
.post(token, &path, None::<&()>, Some(&body))
.await?;
Ok(serde_json::from_value(response)?)
}
#[deprecated(since = "0.1.0", note = "Use post_message_with_params instead")]
pub async fn post_message(
&self,
token: &Token,
channel_id: &str,
content: Option<&str>,
embed: Option<&Embed>,
ark: Option<&Ark>,
message_reference: Option<&Reference>,
image: Option<&str>,
file_image: Option<&[u8]>,
msg_id: Option<&str>,
event_id: Option<&str>,
markdown: Option<&MarkdownPayload>,
keyboard: Option<&Keyboard>,
) -> Result<MessageResponse> {
let params = MessageParams {
content: content.map(|s| s.to_string()),
embed: embed.cloned(),
ark: ark.cloned(),
message_reference: message_reference.cloned(),
image: image.map(|s| s.to_string()),
file_image: file_image
.map(|data| base64::engine::general_purpose::STANDARD.encode(data)),
msg_id: msg_id.map(|s| s.to_string()),
event_id: event_id.map(|s| s.to_string()),
markdown: markdown.cloned(),
keyboard: keyboard.cloned(),
};
self.post_message_with_params(token, channel_id, params)
.await
}
pub async fn post_group_message_with_params(
&self,
token: &Token,
group_openid: &str,
params: GroupMessageParams,
) -> Result<MessageResponse> {
debug!("Sending group message to {}", group_openid);
let body = serde_json::to_value(¶ms)?;
let path = format!("/v2/groups/{group_openid}/messages");
let response = self
.http
.post(token, &path, None::<&()>, Some(&body))
.await?;
Ok(serde_json::from_value(response)?)
}
#[deprecated(since = "0.1.0", note = "Use post_group_message_with_params instead")]
pub async fn post_group_message(
&self,
token: &Token,
group_openid: &str,
msg_type: Option<u32>,
content: Option<&str>,
embed: Option<&Embed>,
ark: Option<&Ark>,
message_reference: Option<&Reference>,
media: Option<&Media>,
msg_id: Option<&str>,
msg_seq: Option<u32>,
event_id: Option<&str>,
markdown: Option<&MarkdownPayload>,
keyboard: Option<&KeyboardPayload>,
) -> Result<MessageResponse> {
let params = GroupMessageParams {
msg_type: msg_type.unwrap_or(0),
content: content.map(|s| s.to_string()),
embed: embed.cloned(),
ark: ark.cloned(),
message_reference: message_reference.cloned(),
media: media.cloned(),
msg_id: msg_id.map(|s| s.to_string()),
msg_seq,
event_id: event_id.map(|s| s.to_string()),
markdown: markdown.cloned(),
keyboard: keyboard.cloned(),
};
self.post_group_message_with_params(token, group_openid, params)
.await
}
pub async fn post_c2c_message_with_params(
&self,
token: &Token,
openid: &str,
params: C2CMessageParams,
) -> Result<MessageResponse> {
debug!("Sending C2C message to {}", openid);
let body = serde_json::to_value(¶ms)?;
let path = format!("/v2/users/{openid}/messages");
let response = self
.http
.post(token, &path, None::<&()>, Some(&body))
.await?;
Ok(serde_json::from_value(response)?)
}
#[deprecated(since = "0.1.0", note = "Use post_c2c_message_with_params instead")]
pub async fn post_c2c_message(
&self,
token: &Token,
openid: &str,
msg_type: Option<u32>,
content: Option<&str>,
embed: Option<&Embed>,
ark: Option<&Ark>,
message_reference: Option<&Reference>,
media: Option<&Media>,
msg_id: Option<&str>,
msg_seq: Option<u32>,
event_id: Option<&str>,
markdown: Option<&MarkdownPayload>,
keyboard: Option<&KeyboardPayload>,
) -> Result<MessageResponse> {
let params = C2CMessageParams {
msg_type: msg_type.unwrap_or(0),
content: content.map(|s| s.to_string()),
embed: embed.cloned(),
ark: ark.cloned(),
message_reference: message_reference.cloned(),
media: media.cloned(),
msg_id: msg_id.map(|s| s.to_string()),
msg_seq,
event_id: event_id.map(|s| s.to_string()),
markdown: markdown.cloned(),
keyboard: keyboard.cloned(),
};
self.post_c2c_message_with_params(token, openid, params)
.await
}
pub async fn post_dms_with_params(
&self,
token: &Token,
guild_id: &str,
params: DirectMessageParams,
) -> Result<MessageResponse> {
debug!("Sending direct message to guild session {}", guild_id);
let body = serde_json::to_value(¶ms)?;
let path = format!("/dms/{guild_id}/messages");
let response = self
.http
.post(token, &path, None::<&()>, Some(&body))
.await?;
Ok(serde_json::from_value(response)?)
}
#[deprecated(since = "0.1.0", note = "Use post_dms_with_params instead")]
pub async fn post_dms(
&self,
token: &Token,
guild_id: &str,
content: Option<&str>,
embed: Option<&Embed>,
ark: Option<&Ark>,
message_reference: Option<&Reference>,
image: Option<&str>,
file_image: Option<&[u8]>,
msg_id: Option<&str>,
event_id: Option<&str>,
markdown: Option<&MarkdownPayload>,
keyboard: Option<&Keyboard>,
) -> Result<MessageResponse> {
let params = DirectMessageParams {
content: content.map(|s| s.to_string()),
embed: embed.cloned(),
ark: ark.cloned(),
message_reference: message_reference.cloned(),
image: image.map(|s| s.to_string()),
file_image: file_image
.map(|data| base64::engine::general_purpose::STANDARD.encode(data)),
msg_id: msg_id.map(|s| s.to_string()),
event_id: event_id.map(|s| s.to_string()),
markdown: markdown.cloned(),
keyboard: keyboard.cloned(),
};
self.post_dms_with_params(token, guild_id, params).await
}
pub async fn create_dms(&self, token: &Token, guild_id: &str, user_id: &str) -> Result<Value> {
debug!(
"Creating DM session for user {} from guild {}",
user_id, guild_id
);
let body = json!({
"recipient_id": user_id,
"source_guild_id": guild_id
});
let response = self
.http
.post(token, "/users/@me/dms", None::<&()>, Some(&body))
.await?;
Ok(response)
}
pub async fn recall_message(
&self,
token: &Token,
channel_id: &str,
message_id: &str,
hidetip: Option<bool>,
) -> Result<()> {
debug!("Recalling message {} in channel {}", message_id, channel_id);
let mut params = HashMap::new();
params.insert(
"hidetip",
if hidetip.unwrap_or(false) {
"true"
} else {
"false"
}
.to_string(),
);
let path = format!("/channels/{channel_id}/messages/{message_id}");
self.http.delete(token, &path, Some(¶ms)).await?;
Ok(())
}
pub async fn update_audio(
&self,
token: &Token,
channel_id: &str,
audio_control: &AudioAction,
) -> Result<()> {
debug!("Updating audio in channel {}", channel_id);
let path = format!("/channels/{channel_id}/audio");
let _response = self
.http
.post(token, &path, None::<&()>, Some(audio_control))
.await?;
Ok(())
}
pub async fn on_microphone(&self, token: &Token, channel_id: &str) -> Result<()> {
debug!("Turning on microphone in channel {}", channel_id);
let path = format!("/channels/{channel_id}/mic");
self.http
.put(token, &path, None::<&()>, None::<&()>)
.await?;
Ok(())
}
pub async fn off_microphone(&self, token: &Token, channel_id: &str) -> Result<()> {
debug!("Turning off microphone in channel {}", channel_id);
let path = format!("/channels/{channel_id}/mic");
self.http.delete(token, &path, None::<&()>).await?;
Ok(())
}
pub async fn mute_all(
&self,
token: &Token,
guild_id: &str,
mute_end_timestamp: Option<&str>,
mute_seconds: Option<&str>,
) -> Result<()> {
debug!("Muting all members in guild {}", guild_id);
let body = json!({
"mute_end_timestamp": mute_end_timestamp,
"mute_seconds": mute_seconds
});
let path = format!("/guilds/{guild_id}/mute");
self.http
.put(token, &path, None::<&()>, Some(&body))
.await?;
Ok(())
}
pub async fn cancel_mute_all(&self, token: &Token, guild_id: &str) -> Result<()> {
debug!("Canceling mute for all members in guild {}", guild_id);
let body = json!({
"mute_end_timestamp": "0",
"mute_seconds": "0"
});
let path = format!("/guilds/{guild_id}/mute");
self.http
.put(token, &path, None::<&()>, Some(&body))
.await?;
Ok(())
}
pub async fn mute_member(
&self,
token: &Token,
guild_id: &str,
user_id: &str,
mute_end_timestamp: Option<&str>,
mute_seconds: Option<&str>,
) -> Result<()> {
debug!("Muting member {} in guild {}", user_id, guild_id);
let body = json!({
"mute_end_timestamp": mute_end_timestamp,
"mute_seconds": mute_seconds
});
let path = format!("/guilds/{guild_id}/members/{user_id}/mute");
self.http
.put(token, &path, None::<&()>, Some(&body))
.await?;
Ok(())
}
pub async fn get_channel_user_permissions(
&self,
token: &Token,
channel_id: &str,
user_id: &str,
) -> Result<ChannelPermissions> {
debug!(
"Getting channel permissions for user {} in channel {}",
user_id, channel_id
);
let path = format!("/channels/{channel_id}/members/{user_id}/permissions");
let response = self.http.get(token, &path, None::<&()>).await?;
Ok(serde_json::from_value(response)?)
}
pub async fn get_channel_role_permissions(
&self,
token: &Token,
channel_id: &str,
role_id: &str,
) -> Result<ChannelPermissions> {
debug!(
"Getting channel permissions for role {} in channel {}",
role_id, channel_id
);
let path = format!("/channels/{channel_id}/roles/{role_id}/permissions");
let response = self.http.get(token, &path, None::<&()>).await?;
Ok(serde_json::from_value(response)?)
}
pub async fn put_reaction(
&self,
token: &Token,
channel_id: &str,
message_id: &str,
emoji_type: u32,
emoji_id: &str,
) -> Result<()> {
debug!(
"Adding reaction to message {} in channel {}",
message_id, channel_id
);
let path = format!(
"/channels/{channel_id}/messages/{message_id}/reactions/{emoji_type}/{emoji_id}"
);
self.http
.put(token, &path, None::<&()>, None::<&()>)
.await?;
Ok(())
}
pub async fn delete_reaction(
&self,
token: &Token,
channel_id: &str,
message_id: &str,
emoji_type: u32,
emoji_id: &str,
) -> Result<()> {
debug!(
"Removing reaction from message {} in channel {}",
message_id, channel_id
);
let path = format!(
"/channels/{channel_id}/messages/{message_id}/reactions/{emoji_type}/{emoji_id}"
);
self.http.delete(token, &path, None::<&()>).await?;
Ok(())
}
pub async fn put_pin(
&self,
token: &Token,
channel_id: &str,
message_id: &str,
) -> Result<Value> {
debug!("Pinning message {} in channel {}", message_id, channel_id);
let path = format!("/channels/{channel_id}/pins/{message_id}");
let response = self
.http
.put(token, &path, None::<&()>, Some(&json!({})))
.await?;
Ok(response)
}
pub async fn delete_pin(
&self,
token: &Token,
channel_id: &str,
message_id: &str,
) -> Result<()> {
debug!("Unpinning message {} in channel {}", message_id, channel_id);
let path = format!("/channels/{channel_id}/pins/{message_id}");
self.http.delete(token, &path, None::<&()>).await?;
Ok(())
}
pub async fn get_pins(&self, token: &Token, channel_id: &str) -> Result<Value> {
debug!("Getting pinned messages in channel {}", channel_id);
let path = format!("/channels/{channel_id}/pins");
let response = self.http.get(token, &path, None::<&()>).await?;
Ok(response)
}
pub async fn post_group_file(
&self,
token: &Token,
group_openid: &str,
file_type: u32,
url: &str,
srv_send_msg: Option<bool>,
) -> Result<Value> {
debug!("Uploading group file to {}", group_openid);
let body = json!({
"file_type": file_type,
"url": url,
"srv_send_msg": srv_send_msg.unwrap_or(false)
});
let path = format!("/v2/groups/{group_openid}/files");
let response = self
.http
.post(token, &path, None::<&()>, Some(&body))
.await?;
Ok(response)
}
pub async fn post_c2c_file(
&self,
token: &Token,
openid: &str,
file_type: u32,
url: &str,
srv_send_msg: Option<bool>,
) -> Result<Value> {
debug!("Uploading C2C file to {}", openid);
let body = json!({
"file_type": file_type,
"url": url,
"srv_send_msg": srv_send_msg.unwrap_or(false)
});
let path = format!("/v2/users/{openid}/files");
let response = self
.http
.post(token, &path, None::<&()>, Some(&body))
.await?;
Ok(response)
}
pub async fn create_announce(
&self,
token: &Token,
guild_id: &str,
channel_id: &str,
message_id: &str,
) -> Result<Announce> {
debug!(
"Creating announcement in guild {} for message {}",
guild_id, message_id
);
let body = json!({
"channel_id": channel_id,
"message_id": message_id
});
let path = format!("/guilds/{guild_id}/announces");
let response = self
.http
.post(token, &path, None::<&()>, Some(&body))
.await?;
Ok(serde_json::from_value(response)?)
}
pub async fn create_recommend_announce(
&self,
token: &Token,
guild_id: &str,
announces_type: AnnouncesType,
recommend_channels: Vec<RecommendChannel>,
) -> Result<Announce> {
debug!("Creating recommend announcement in guild {}", guild_id);
let body = json!({
"announces_type": u8::from(announces_type),
"recommend_channels": recommend_channels
});
let path = format!("/guilds/{guild_id}/announces");
let response = self
.http
.post(token, &path, None::<&()>, Some(&body))
.await?;
Ok(serde_json::from_value(response)?)
}
pub async fn delete_announce(
&self,
token: &Token,
guild_id: &str,
message_id: &str,
) -> Result<Value> {
debug!("Deleting announcement {} in guild {}", message_id, guild_id);
let path = format!("/guilds/{guild_id}/announces/{message_id}");
let response = self.http.delete(token, &path, None::<&()>).await?;
Ok(response)
}
pub async fn get_permissions(
&self,
token: &Token,
guild_id: &str,
) -> Result<Vec<APIPermission>> {
debug!("Getting permissions for guild {}", guild_id);
let path = format!("/guilds/{guild_id}/api_permission");
let response = self.http.get(token, &path, None::<&()>).await?;
if let Some(apis) = response.get("apis") {
Ok(serde_json::from_value(apis.clone())?)
} else {
Ok(vec![])
}
}
pub async fn post_permission_demand(
&self,
token: &Token,
guild_id: &str,
channel_id: &str,
api_identify: APIPermissionDemandIdentify,
desc: &str,
) -> Result<APIPermissionDemand> {
debug!("Creating permission demand in guild {}", guild_id);
let body = json!({
"channel_id": channel_id,
"api_identify": api_identify,
"desc": desc
});
let path = format!("/guilds/{guild_id}/api_permission/demand");
let response = self
.http
.post(token, &path, None::<&()>, Some(&body))
.await?;
Ok(serde_json::from_value(response)?)
}
pub async fn get_reaction_users(
&self,
token: &Token,
channel_id: &str,
message_id: &str,
emoji_type: EmojiType,
emoji_id: &str,
cookie: Option<&str>,
limit: Option<u32>,
) -> Result<ReactionUsers> {
debug!(
"Getting reaction users for message {} with emoji {}",
message_id, emoji_id
);
let mut params = HashMap::new();
params.insert("limit", limit.unwrap_or(20).to_string());
if let Some(cookie) = cookie {
params.insert("cookie", cookie.to_string());
}
let path = format!(
"/channels/{channel_id}/messages/{message_id}/reactions/{emoji_type}/{emoji_id}",
emoji_type = u8::from(emoji_type)
);
let response = self.http.get(token, &path, Some(¶ms)).await?;
Ok(serde_json::from_value(response)?)
}
pub async fn get_schedules(
&self,
token: &Token,
channel_id: &str,
since: Option<&str>,
) -> Result<Vec<Schedule>> {
debug!("Getting schedules for channel {}", channel_id);
let body = if let Some(since) = since {
json!({ "since": since })
} else {
json!({})
};
let path = format!("/channels/{channel_id}/schedules");
let response = self
.http
.get(
token,
&path,
if since.is_some() { Some(&body) } else { None },
)
.await?;
Ok(serde_json::from_value(response)?)
}
pub async fn get_schedule(
&self,
token: &Token,
channel_id: &str,
schedule_id: &str,
) -> Result<Schedule> {
debug!("Getting schedule {} in channel {}", schedule_id, channel_id);
let path = format!("/channels/{channel_id}/schedules/{schedule_id}");
let response = self.http.get(token, &path, None::<&()>).await?;
Ok(serde_json::from_value(response)?)
}
pub async fn create_schedule(
&self,
token: &Token,
channel_id: &str,
name: &str,
start_timestamp: &str,
end_timestamp: &str,
jump_channel_id: &str,
remind_type: RemindType,
) -> Result<Schedule> {
debug!("Creating schedule '{}' in channel {}", name, channel_id);
let body = json!({
"schedule": {
"name": name,
"start_timestamp": start_timestamp,
"end_timestamp": end_timestamp,
"jump_channel_id": jump_channel_id,
"reminder_id": u8::from(remind_type)
}
});
let path = format!("/channels/{channel_id}/schedules");
let response = self
.http
.post(token, &path, None::<&()>, Some(&body))
.await?;
Ok(serde_json::from_value(response)?)
}
pub async fn update_schedule(
&self,
token: &Token,
channel_id: &str,
schedule_id: &str,
name: &str,
start_timestamp: &str,
end_timestamp: &str,
jump_channel_id: &str,
remind_type: RemindType,
) -> Result<Schedule> {
debug!(
"Updating schedule {} in channel {}",
schedule_id, channel_id
);
let body = json!({
"schedule": {
"name": name,
"start_timestamp": start_timestamp,
"end_timestamp": end_timestamp,
"jump_channel_id": jump_channel_id,
"reminder_id": u8::from(remind_type)
}
});
let path = format!("/channels/{channel_id}/schedules/{schedule_id}");
let response = self
.http
.patch(token, &path, None::<&()>, Some(&body))
.await?;
Ok(serde_json::from_value(response)?)
}
pub async fn delete_schedule(
&self,
token: &Token,
channel_id: &str,
schedule_id: &str,
) -> Result<Value> {
debug!(
"Deleting schedule {} in channel {}",
schedule_id, channel_id
);
let path = format!("/channels/{channel_id}/schedules/{schedule_id}");
let response = self.http.delete(token, &path, None::<&()>).await?;
Ok(response)
}
pub fn http(&self) -> &HttpClient {
&self.http
}
pub async fn close(&self) {
self.http.close().await;
}
}
impl std::fmt::Debug for BotApi {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("BotApi").field("http", &self.http).finish()
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::http::HttpClient;
#[test]
fn test_api_creation() {
let http = HttpClient::new(30, false).unwrap();
let api = BotApi::new(http);
assert!(!api.http().is_sandbox());
}
}