use crate::error::Result;
use crate::http::HttpClient;
use crate::models::{
announce::{Announce, AnnouncesType, RecommendChannel},
api::{AudioAction, BotInfo, GatewayResponse, MessageResponse, PinsMessage},
channel::{
Channel, ChannelPermissions, ChannelRolesPermissions, ChannelSubType, ChannelType,
ChannelValueObject, PrivateType, SpeakPermission, UpdateChannelPermissions,
},
emoji::EmojiType,
guild::{
Guild, GuildMembersPager, GuildPager, GuildRole, GuildRoleMembers, GuildRoleMembersPager,
GuildRoles, Member, MemberAddRoleBody, MemberDeleteOptions, UpdateGuildMute,
UpdateGuildMuteResponse, UpdateResult, UpdateRole, normalize_delete_history_msg_days,
},
message::{
ApiMessage, Ark, C2CMessageParams, DirectMessageParams, DirectMessageSession,
DirectMessageToCreate, Embed, GroupMessageParams, Keyboard, KeyboardPayload,
MarkdownPayload, Media, Message, MessagePagerType, MessageParams, MessageToCreate,
MessagesPager, Reference, RichMediaMessage, SendType, SettingGuide, SettingGuideToCreate,
},
message_setting::MessageSetting,
permission::{
APIPermission, APIPermissionDemand, APIPermissionDemandIdentify,
APIPermissionDemandToCreate, APIPermissions,
},
schedule::{RemindType, Schedule, ScheduleWrapper},
webhook::{
HttpIdentity, HttpReady, HttpSession, WebhookValidationRequest, WebhookValidationResponse,
},
};
use crate::reaction::{Emoji as ReactionEmoji, MessageReactionPager, ReactionUsers};
use crate::token::Token;
use base64::Engine;
use reqwest::header::{HeaderMap, HeaderValue};
use serde::Serialize;
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_message_setting(
&self,
token: &Token,
guild_id: &str,
) -> Result<MessageSetting> {
debug!("Getting message setting for guild {}", guild_id);
let path = format!("/guilds/{guild_id}/message/setting");
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>> {
let mut pager = GuildPager::new();
if let Some(limit) = limit {
pager = pager.with_limit(limit);
}
if let Some(guild_id) = guild_id {
pager = if desc.unwrap_or(false) {
pager.with_before(guild_id)
} else {
pager.with_after(guild_id)
};
}
self.get_guilds_with_pager(token, &pager).await
}
pub async fn get_guilds_with_pager(
&self,
token: &Token,
pager: &GuildPager,
) -> Result<Vec<Guild>> {
debug!("Getting guilds");
let params = pager.query_params();
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_with_update(
&self,
token: &Token,
guild_id: &str,
role: GuildRole,
) -> Result<UpdateResult> {
debug!("Creating guild role in {}", guild_id);
let body = UpdateRole::new(guild_id, role);
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 create_guild_role(
&self,
token: &Token,
guild_id: &str,
name: Option<&str>,
color: Option<u32>,
hoist: Option<bool>,
) -> Result<GuildRole> {
let role = GuildRole {
id: None,
name: name.map(String::from),
color,
hoist,
number: None,
member_limit: None,
};
let result = self
.create_guild_role_with_update(token, guild_id, role)
.await?;
Ok(result.role.unwrap_or_default())
}
pub async fn update_guild_role_with_update(
&self,
token: &Token,
guild_id: &str,
role_id: &str,
role: GuildRole,
) -> Result<UpdateResult> {
debug!("Updating guild role {} in {}", role_id, guild_id);
let body = UpdateRole::new(guild_id, role);
let path = format!("/guilds/{guild_id}/roles/{role_id}");
let response = self
.http
.patch(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> {
let role = GuildRole {
id: Some(role_id.to_string()),
name: name.map(String::from),
color,
hoist,
number: None,
member_limit: None,
};
let result = self
.update_guild_role_with_update(token, guild_id, role_id, role)
.await?;
Ok(result.role.unwrap_or_default())
}
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 {
MemberAddRoleBody::with_channel_id(channel_id)
} else {
MemberAddRoleBody::new()
};
self.member_add_role(token, guild_id, role_id, user_id, &body)
.await?;
Ok(())
}
pub async fn member_add_role(
&self,
token: &Token,
guild_id: &str,
role_id: &str,
user_id: &str,
body: &MemberAddRoleBody,
) -> Result<()> {
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 {
MemberAddRoleBody::with_channel_id(channel_id)
} else {
MemberAddRoleBody::new()
};
self.member_delete_role(token, guild_id, role_id, user_id, &body)
.await?;
Ok(())
}
pub async fn member_delete_role(
&self,
token: &Token,
guild_id: &str,
role_id: &str,
user_id: &str,
body: &MemberAddRoleBody,
) -> Result<()> {
let path = format!("/guilds/{guild_id}/members/{user_id}/roles/{role_id}");
self.http
.delete_with_body(token, &path, None::<&()>, 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>> {
let pager = GuildMembersPager::new(after.unwrap_or("0"), limit.unwrap_or(1).to_string());
self.get_guild_members_with_pager(token, guild_id, &pager)
.await
}
pub async fn get_guild_members_with_pager(
&self,
token: &Token,
guild_id: &str,
pager: &GuildMembersPager,
) -> Result<Vec<Member>> {
debug!(
"Getting guild members for {} with pager {:?}",
guild_id, pager
);
let path = format!("/guilds/{guild_id}/members");
let response = self.http.get(token, &path, Some(pager)).await?;
Ok(serde_json::from_value(response)?)
}
pub async fn get_guild_role_members(
&self,
token: &Token,
guild_id: &str,
role_id: &str,
start_index: Option<&str>,
limit: Option<u32>,
) -> Result<GuildRoleMembers> {
let pager =
GuildRoleMembersPager::new(start_index.unwrap_or("0"), limit.unwrap_or(1).to_string());
self.get_guild_role_members_with_pager(token, guild_id, role_id, &pager)
.await
}
pub async fn get_guild_role_members_with_pager(
&self,
token: &Token,
guild_id: &str,
role_id: &str,
pager: &GuildRoleMembersPager,
) -> Result<GuildRoleMembers> {
debug!(
"Getting role {} members for guild {} with pager {:?}",
role_id, guild_id, pager
);
let path = format!("/guilds/{guild_id}/roles/{role_id}/members");
let response = self.http.get(token, &path, Some(pager)).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<()> {
let options = MemberDeleteOptions {
add_blacklist: add_blacklist.unwrap_or(false),
delete_history_msg_days: normalize_delete_history_msg_days(
delete_history_msg_days.unwrap_or(0),
),
};
self.delete_member_with_options(token, guild_id, user_id, &options)
.await
}
pub async fn delete_member_with_options(
&self,
token: &Token,
guild_id: &str,
user_id: &str,
options: &MemberDeleteOptions,
) -> Result<()> {
debug!("Deleting member {} from guild {}", user_id, guild_id);
let path = format!("/guilds/{guild_id}/members/{user_id}");
self.http
.delete_with_body(token, &path, None::<&()>, Some(options))
.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 post_channel(
&self,
token: &Token,
guild_id: &str,
value: &ChannelValueObject,
) -> Result<Channel> {
debug!("Creating channel in guild {}", guild_id);
let path = format!("/guilds/{guild_id}/channels");
let response = self
.http
.post(token, &path, None::<&()>, Some(value))
.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> {
let value = ChannelValueObject {
name: Some(name.to_string()),
channel_type: Some(channel_type),
sub_type: Some(sub_type),
position: position.map(i64::from),
parent_id: parent_id.map(String::from),
private_type: private_type.map(|value| PrivateType::from(value as u8)),
private_user_ids,
speak_permission: speak_permission.map(|value| SpeakPermission::from(value as u8)),
application_id: application_id.map(String::from),
..Default::default()
};
self.post_channel(token, guild_id, &value).await
}
pub async fn create_private_channel(
&self,
token: &Token,
guild_id: &str,
value: &ChannelValueObject,
user_ids: Vec<String>,
) -> Result<Channel> {
let mut value = value.clone();
value.private_type = Some(PrivateType::AdminAndSpecifiedMembers);
if !user_ids.is_empty() {
value.private_user_ids = Some(user_ids);
value.private_type = Some(PrivateType::AdminOnly);
}
self.post_channel(token, guild_id, &value).await
}
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> {
let value = ChannelValueObject {
name: name.map(String::from),
position: position.map(i64::from),
parent_id: parent_id.map(String::from),
private_type: private_type.map(|value| PrivateType::from(value as u8)),
speak_permission: speak_permission.map(|value| SpeakPermission::from(value as u8)),
..Default::default()
};
self.patch_channel(token, channel_id, &value).await
}
pub async fn patch_channel(
&self,
token: &Token,
channel_id: &str,
value: &ChannelValueObject,
) -> Result<Channel> {
debug!("Updating channel {}", channel_id);
let path = format!("/channels/{channel_id}");
let response = self
.http
.patch(token, &path, None::<&()>, Some(value))
.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 list_voice_channel_members(
&self,
token: &Token,
channel_id: &str,
) -> Result<Vec<Member>> {
debug!("Listing voice channel members for channel {}", channel_id);
let path = format!("/channels/{channel_id}/voice/members");
let response = self.http.get(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 get_messages(
&self,
token: &Token,
channel_id: &str,
pager: &MessagesPager,
) -> Result<Vec<Message>> {
debug!("Getting messages in channel {}", channel_id);
let params = pager.query_params();
let path = format!("/channels/{channel_id}/messages");
let response = self
.http
.get(
token,
&path,
if params.is_empty() {
None
} else {
Some(¶ms)
},
)
.await?;
Ok(serde_json::from_value(response)?)
}
pub async fn get_messages_with_params(
&self,
token: &Token,
channel_id: &str,
pager_type: Option<MessagePagerType>,
message_id: Option<&str>,
limit: Option<u32>,
) -> Result<Vec<Message>> {
let pager = MessagesPager::new(pager_type, message_id, limit);
self.get_messages(token, channel_id, &pager).await
}
pub async fn post_message_to_create(
&self,
token: &Token,
channel_id: &str,
msg: &MessageToCreate,
) -> Result<Message> {
debug!("Sending botgo-style message to channel {}", channel_id);
let path = format!("/channels/{channel_id}/messages");
let response = self.http.post(token, &path, None::<&()>, Some(msg)).await?;
Ok(serde_json::from_value(response)?)
}
pub async fn post_message_api(
&self,
token: &Token,
channel_id: &str,
msg: &MessageToCreate,
) -> Result<Message> {
self.post_message_to_create(token, channel_id, msg).await
}
pub async fn patch_message_to_create(
&self,
token: &Token,
channel_id: &str,
message_id: &str,
msg: &MessageToCreate,
) -> Result<Message> {
debug!("Editing message {} in channel {}", message_id, channel_id);
let path = format!("/channels/{channel_id}/messages/{message_id}");
let response = self
.http
.patch(token, &path, None::<&()>, Some(msg))
.await?;
Ok(serde_json::from_value(response)?)
}
pub async fn patch_message_api(
&self,
token: &Token,
channel_id: &str,
message_id: &str,
msg: &MessageToCreate,
) -> Result<Message> {
self.patch_message_to_create(token, channel_id, message_id, msg)
.await
}
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(MessageToCreate::from(params))?;
let path = format!("/channels/{channel_id}/messages");
let response = self
.http
.post(token, &path, None::<&()>, Some(&body))
.await?;
Ok(serde_json::from_value(response)?)
}
pub async fn patch_message_with_params(
&self,
token: &Token,
channel_id: &str,
message_id: &str,
params: MessageParams,
) -> Result<MessageResponse> {
debug!("Editing message {} in channel {}", message_id, channel_id);
let body = serde_json::to_value(MessageToCreate::from(params))?;
let path = format!("/channels/{channel_id}/messages/{message_id}");
let response = self
.http
.patch(token, &path, None::<&()>, Some(&body))
.await?;
Ok(serde_json::from_value(response)?)
}
pub async fn patch_message(
&self,
token: &Token,
channel_id: &str,
message_id: &str,
params: MessageParams,
) -> Result<MessageResponse> {
self.patch_message_with_params(token, channel_id, message_id, params)
.await
}
#[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()),
msg_type: None,
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(),
timestamp: None,
msg_seq: None,
subscribe_id: None,
input_notify: None,
media: None,
prompt_keyboard: None,
action_button: None,
stream: None,
feature_id: None,
};
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(MessageToCreate::from(params))?;
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)?)
}
pub async fn post_group_api_message(
&self,
token: &Token,
group_openid: &str,
msg: &ApiMessage,
) -> Result<Message> {
debug!("Sending botgo-style group message to {}", group_openid);
self.post_group_api_payload(token, group_openid, msg.send_type(), msg)
.await
}
pub async fn post_group_message_to_create(
&self,
token: &Token,
group_openid: &str,
msg: &MessageToCreate,
) -> Result<Message> {
self.post_group_api_payload(token, group_openid, msg.send_type(), msg)
.await
}
pub async fn post_group_rich_media_message(
&self,
token: &Token,
group_openid: &str,
msg: &RichMediaMessage,
) -> Result<Message> {
self.post_group_api_payload(token, group_openid, msg.send_type(), msg)
.await
}
async fn post_group_api_payload<T: Serialize + ?Sized>(
&self,
token: &Token,
group_openid: &str,
send_type: SendType,
msg: &T,
) -> Result<Message> {
let path = match send_type {
SendType::RichMedia => format!("/v2/groups/{group_openid}/files"),
_ => format!("/v2/groups/{group_openid}/messages"),
};
let response = self.http.post(token, &path, None::<&()>, Some(msg)).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(),
timestamp: None,
subscribe_id: None,
input_notify: None,
prompt_keyboard: None,
action_button: None,
stream: None,
feature_id: None,
};
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(MessageToCreate::from(params))?;
let path = format!("/v2/users/{openid}/messages");
let response = self
.http
.post(token, &path, None::<&()>, Some(&body))
.await?;
Ok(serde_json::from_value(response)?)
}
pub async fn post_c2c_api_message(
&self,
token: &Token,
openid: &str,
msg: &ApiMessage,
) -> Result<Message> {
debug!("Sending botgo-style C2C message to {}", openid);
self.post_c2c_api_payload(token, openid, msg.send_type(), msg)
.await
}
pub async fn post_c2c_message_to_create(
&self,
token: &Token,
openid: &str,
msg: &MessageToCreate,
) -> Result<Message> {
self.post_c2c_api_payload(token, openid, msg.send_type(), msg)
.await
}
pub async fn post_c2c_rich_media_message(
&self,
token: &Token,
openid: &str,
msg: &RichMediaMessage,
) -> Result<Message> {
self.post_c2c_api_payload(token, openid, msg.send_type(), msg)
.await
}
async fn post_c2c_api_payload<T: Serialize + ?Sized>(
&self,
token: &Token,
openid: &str,
send_type: SendType,
msg: &T,
) -> Result<Message> {
let path = match send_type {
SendType::RichMedia => format!("/v2/users/{openid}/files"),
_ => format!("/v2/users/{openid}/messages"),
};
let response = self.http.post(token, &path, None::<&()>, Some(msg)).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(),
timestamp: None,
subscribe_id: None,
input_notify: None,
prompt_keyboard: None,
action_button: None,
stream: None,
feature_id: None,
};
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(MessageToCreate::from(params))?;
let path = format!("/dms/{guild_id}/messages");
let response = self
.http
.post(token, &path, None::<&()>, Some(&body))
.await?;
Ok(serde_json::from_value(response)?)
}
pub async fn post_direct_message(
&self,
token: &Token,
guild_id: &str,
msg: &MessageToCreate,
) -> Result<Message> {
debug!("Sending botgo-style direct message to guild {}", guild_id);
let path = format!("/dms/{guild_id}/messages");
let response = self.http.post(token, &path, None::<&()>, Some(msg)).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()),
msg_type: None,
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(),
timestamp: None,
msg_seq: None,
subscribe_id: None,
input_notify: None,
media: None,
prompt_keyboard: None,
action_button: None,
stream: None,
feature_id: None,
};
self.post_dms_with_params(token, guild_id, params).await
}
pub async fn create_direct_message(
&self,
token: &Token,
dm: &DirectMessageToCreate,
) -> Result<DirectMessageSession> {
debug!(
"Creating DM session for user {} from guild {}",
dm.recipient_id, dm.source_guild_id
);
let response = self
.http
.post(token, "/users/@me/dms", None::<&()>, Some(dm))
.await?;
Ok(serde_json::from_value(response)?)
}
pub async fn create_dms(
&self,
token: &Token,
guild_id: &str,
user_id: &str,
) -> Result<DirectMessageSession> {
let dm = DirectMessageToCreate::new(guild_id, user_id);
self.create_direct_message(token, &dm).await
}
pub async fn post_setting_guide(
&self,
token: &Token,
channel_id: &str,
at_user_ids: Vec<String>,
) -> Result<MessageResponse> {
let content = at_user_ids
.iter()
.map(|user_id| format!("<@{user_id}>"))
.collect::<String>();
let body = SettingGuideToCreate {
content: Some(content),
setting_guide: None,
};
let path = format!("/channels/{channel_id}/settingguide");
let response = self
.http
.post(token, &path, None::<&()>, Some(&body))
.await?;
Ok(serde_json::from_value(response)?)
}
pub async fn post_setting_guide_message(
&self,
token: &Token,
channel_id: &str,
at_user_ids: Vec<String>,
) -> Result<Message> {
let content = at_user_ids
.iter()
.map(|user_id| format!("<@{user_id}>"))
.collect::<String>();
let body = SettingGuideToCreate {
content: Some(content),
setting_guide: None,
};
let path = format!("/channels/{channel_id}/settingguide");
let response = self
.http
.post(token, &path, None::<&()>, Some(&body))
.await?;
Ok(serde_json::from_value(response)?)
}
pub async fn post_dm_setting_guide(
&self,
token: &Token,
guild_id: &str,
jump_guild_id: &str,
) -> Result<MessageResponse> {
let body = SettingGuideToCreate {
content: None,
setting_guide: Some(SettingGuide {
guild_id: jump_guild_id.to_string(),
}),
};
let path = format!("/dms/{guild_id}/settingguide");
let response = self
.http
.post(token, &path, None::<&()>, Some(&body))
.await?;
Ok(serde_json::from_value(response)?)
}
pub async fn post_dm_setting_guide_message(
&self,
token: &Token,
guild_id: &str,
jump_guild_id: &str,
) -> Result<Message> {
let body = SettingGuideToCreate {
content: None,
setting_guide: Some(SettingGuide {
guild_id: jump_guild_id.to_string(),
}),
};
let path = format!("/dms/{guild_id}/settingguide");
let response = self
.http
.post(token, &path, None::<&()>, Some(&body))
.await?;
Ok(serde_json::from_value(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 retract_c2c_message(
&self,
token: &Token,
openid: &str,
message_id: &str,
hidetip: Option<bool>,
) -> Result<()> {
debug!("Retracting C2C message {} for {}", message_id, openid);
let mut params = HashMap::new();
if hidetip.unwrap_or(false) {
params.insert("hidetip", "true".to_string());
}
let path = format!("/v2/users/{openid}/messages/{message_id}");
self.http
.delete(
token,
&path,
if params.is_empty() {
None
} else {
Some(¶ms)
},
)
.await?;
Ok(())
}
pub async fn retract_group_message(
&self,
token: &Token,
group_openid: &str,
message_id: &str,
hidetip: Option<bool>,
) -> Result<()> {
debug!(
"Retracting group message {} for {}",
message_id, group_openid
);
let mut params = HashMap::new();
if hidetip.unwrap_or(false) {
params.insert("hidetip", "true".to_string());
}
let path = format!("/v2/groups/{group_openid}/messages/{message_id}");
self.http
.delete(
token,
&path,
if params.is_empty() {
None
} else {
Some(¶ms)
},
)
.await?;
Ok(())
}
pub async fn retract_dm_message(
&self,
token: &Token,
guild_id: &str,
message_id: &str,
hidetip: Option<bool>,
) -> Result<()> {
debug!("Retracting DM message {} in {}", message_id, guild_id);
let mut params = HashMap::new();
if hidetip.unwrap_or(false) {
params.insert("hidetip", "true".to_string());
}
let path = format!("/dms/{guild_id}/messages/{message_id}");
self.http
.delete(
token,
&path,
if params.is_empty() {
None
} else {
Some(¶ms)
},
)
.await?;
Ok(())
}
pub async fn update_audio(
&self,
token: &Token,
channel_id: &str,
audio_control: &AudioAction,
) -> Result<()> {
self.post_audio(token, channel_id, audio_control).await?;
Ok(())
}
pub async fn post_audio(
&self,
token: &Token,
channel_id: &str,
audio_control: &AudioAction,
) -> Result<AudioAction> {
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(audio_control.clone())
}
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 = UpdateGuildMute::new(mute_end_timestamp, mute_seconds);
let path = format!("/guilds/{guild_id}/mute");
self.http
.patch(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 = UpdateGuildMute::cancel();
let path = format!("/guilds/{guild_id}/mute");
self.http
.patch(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 = UpdateGuildMute::new(mute_end_timestamp, mute_seconds);
let path = format!("/guilds/{guild_id}/members/{user_id}/mute");
self.http
.patch(token, &path, None::<&()>, Some(&body))
.await?;
Ok(())
}
pub async fn mute_multi_member(
&self,
token: &Token,
guild_id: &str,
user_ids: Vec<String>,
mute_end_timestamp: Option<&str>,
mute_seconds: Option<&str>,
) -> Result<UpdateGuildMuteResponse> {
if user_ids.is_empty() {
return Err(crate::error::BotError::invalid_data("no user id param"));
}
let body = UpdateGuildMute::new_multi(user_ids, mute_end_timestamp, mute_seconds);
self.multi_member_mute(token, guild_id, &body).await
}
pub async fn cancel_mute_multi_member(
&self,
token: &Token,
guild_id: &str,
user_ids: Vec<String>,
) -> Result<UpdateGuildMuteResponse> {
if user_ids.is_empty() {
return Err(crate::error::BotError::invalid_data("no user id param"));
}
let body = UpdateGuildMute::cancel_multi(user_ids);
self.multi_member_mute(token, guild_id, &body).await
}
pub async fn multi_member_mute(
&self,
token: &Token,
guild_id: &str,
mute: &UpdateGuildMute,
) -> Result<UpdateGuildMuteResponse> {
if mute.user_ids.as_ref().is_none_or(Vec::is_empty) {
return Err(crate::error::BotError::invalid_data("no user id param"));
}
debug!("Muting multiple members in guild {}", guild_id);
let path = format!("/guilds/{guild_id}/mute");
let response = self
.http
.patch(token, &path, None::<&()>, Some(mute))
.await?;
Ok(serde_json::from_value(response)?)
}
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 put_channel_permissions(
&self,
token: &Token,
channel_id: &str,
user_id: &str,
permissions: &UpdateChannelPermissions,
) -> Result<()> {
permissions.validate()?;
debug!(
"Updating channel permissions for user {} in channel {}",
user_id, channel_id
);
let path = format!("/channels/{channel_id}/members/{user_id}/permissions");
self.http
.put(token, &path, None::<&()>, Some(permissions))
.await?;
Ok(())
}
pub async fn update_channel_user_permissions(
&self,
token: &Token,
channel_id: &str,
user_id: &str,
add: Option<&str>,
remove: Option<&str>,
) -> Result<()> {
let permissions = UpdateChannelPermissions::new(add, remove);
self.put_channel_permissions(token, channel_id, user_id, &permissions)
.await
}
pub async fn get_channel_role_permissions(
&self,
token: &Token,
channel_id: &str,
role_id: &str,
) -> Result<ChannelRolesPermissions> {
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_channel_roles_permissions(
&self,
token: &Token,
channel_id: &str,
role_id: &str,
permissions: &UpdateChannelPermissions,
) -> Result<()> {
permissions.validate()?;
debug!(
"Updating channel permissions for role {} in channel {}",
role_id, channel_id
);
let path = format!("/channels/{channel_id}/roles/{role_id}/permissions");
self.http
.put(token, &path, None::<&()>, Some(permissions))
.await?;
Ok(())
}
pub async fn update_channel_role_permissions(
&self,
token: &Token,
channel_id: &str,
role_id: &str,
add: Option<&str>,
remove: Option<&str>,
) -> Result<()> {
let permissions = UpdateChannelPermissions::new(add, remove);
self.put_channel_roles_permissions(token, channel_id, role_id, &permissions)
.await
}
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 create_message_reaction(
&self,
token: &Token,
channel_id: &str,
message_id: &str,
emoji: &ReactionEmoji,
) -> Result<()> {
let emoji_type = emoji.emoji_type.unwrap_or(0);
let emoji_id = emoji.id.as_deref().unwrap_or_default();
self.put_reaction(
token,
channel_id,
message_id,
u32::from(emoji_type),
emoji_id,
)
.await
}
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 delete_own_message_reaction(
&self,
token: &Token,
channel_id: &str,
message_id: &str,
emoji: &ReactionEmoji,
) -> Result<()> {
let emoji_type = emoji.emoji_type.unwrap_or(0);
let emoji_id = emoji.id.as_deref().unwrap_or_default();
self.delete_reaction(
token,
channel_id,
message_id,
u32::from(emoji_type),
emoji_id,
)
.await
}
pub async fn put_interaction(
&self,
token: &Token,
interaction_id: &str,
body: &str,
) -> Result<()> {
debug!("Updating interaction {}", interaction_id);
let mut headers = HeaderMap::new();
let app_id = HeaderValue::from_str(token.app_id())
.map_err(|e| crate::BotError::invalid_data(format!("Invalid app ID header: {e}")))?;
headers.insert("X-Callback-AppID", app_id);
let path = format!("/interactions/{interaction_id}");
self.http
.put_raw_with_headers(token, &path, None::<&()>, body, headers)
.await?;
Ok(())
}
pub async fn put_pin(
&self,
token: &Token,
channel_id: &str,
message_id: &str,
) -> Result<PinsMessage> {
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::<&()>, None::<&()>)
.await?;
Ok(serde_json::from_value(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 clean_pins(&self, token: &Token, channel_id: &str) -> Result<()> {
debug!("Clearing pinned messages in channel {}", channel_id);
let path = format!("/channels/{channel_id}/pins/all");
self.http.delete(token, &path, None::<&()>).await?;
Ok(())
}
pub async fn get_pins(&self, token: &Token, channel_id: &str) -> Result<PinsMessage> {
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(serde_json::from_value(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_channel_announce(
&self,
token: &Token,
channel_id: &str,
message_id: &str,
) -> Result<Announce> {
debug!(
"Creating channel announcement in channel {} for message {}",
channel_id, message_id
);
let body = json!({
"message_id": message_id
});
let path = format!("/channels/{channel_id}/announces");
let response = self
.http
.post(token, &path, None::<&()>, Some(&body))
.await?;
Ok(serde_json::from_value(response)?)
}
pub async fn delete_channel_announce(
&self,
token: &Token,
channel_id: &str,
message_id: &str,
) -> Result<()> {
debug!(
"Deleting announcement {} in channel {}",
message_id, channel_id
);
let path = format!("/channels/{channel_id}/announces/{message_id}");
self.http.delete(token, &path, None::<&()>).await?;
Ok(())
}
pub async fn clean_channel_announces(&self, token: &Token, channel_id: &str) -> Result<()> {
debug!("Clearing announcements in channel {}", channel_id);
let path = format!("/channels/{channel_id}/announces/all");
self.http.delete(token, &path, None::<&()>).await?;
Ok(())
}
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_guild_announce(
&self,
token: &Token,
guild_id: &str,
channel_id: &str,
message_id: &str,
) -> Result<Announce> {
self.create_announce(token, guild_id, channel_id, message_id)
.await
}
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 create_guild_recommend_announce(
&self,
token: &Token,
guild_id: &str,
announces_type: AnnouncesType,
recommend_channels: Vec<RecommendChannel>,
) -> Result<Announce> {
self.create_recommend_announce(token, guild_id, announces_type, recommend_channels)
.await
}
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 delete_guild_announce(
&self,
token: &Token,
guild_id: &str,
message_id: &str,
) -> Result<()> {
self.delete_announce(token, guild_id, message_id).await?;
Ok(())
}
pub async fn clean_guild_announces(&self, token: &Token, guild_id: &str) -> Result<()> {
debug!("Clearing announcements in guild {}", guild_id);
let path = format!("/guilds/{guild_id}/announces/all");
self.http.delete(token, &path, None::<&()>).await?;
Ok(())
}
pub async fn get_api_permissions(
&self,
token: &Token,
guild_id: &str,
) -> Result<APIPermissions> {
debug!("Getting permissions for guild {}", guild_id);
let path = format!("/guilds/{guild_id}/api_permission");
let response = self.http.get(token, &path, None::<&()>).await?;
Ok(serde_json::from_value(response)?)
}
pub async fn get_permissions(
&self,
token: &Token,
guild_id: &str,
) -> Result<Vec<APIPermission>> {
Ok(self.get_api_permissions(token, guild_id).await?.api_list)
}
pub async fn require_api_permissions(
&self,
token: &Token,
guild_id: &str,
demand: &APIPermissionDemandToCreate,
) -> Result<APIPermissionDemand> {
debug!("Creating permission demand in guild {}", guild_id);
let path = format!("/guilds/{guild_id}/api_permission/demand");
let response = self
.http
.post(token, &path, None::<&()>, Some(demand))
.await?;
Ok(serde_json::from_value(response)?)
}
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 demand = APIPermissionDemandToCreate::new(channel_id, api_identify, desc);
self.require_api_permissions(token, guild_id, &demand).await
}
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_message_reaction_users(
&self,
token: &Token,
channel_id: &str,
message_id: &str,
emoji: &ReactionEmoji,
pager: &MessageReactionPager,
) -> Result<ReactionUsers> {
debug!(
"Getting reaction users for message {} with emoji {:?}",
message_id, emoji.id
);
let params = pager.query_params();
let emoji_type = emoji.emoji_type.unwrap_or(0);
let emoji_id = emoji.id.as_deref().unwrap_or_default();
let path = format!(
"/channels/{channel_id}/messages/{message_id}/reactions/{emoji_type}/{emoji_id}"
);
let response = self
.http
.get(
token,
&path,
if params.is_empty() {
None
} else {
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> {
let schedule = Schedule::new(
name,
start_timestamp,
end_timestamp,
Some(jump_channel_id.to_string()),
remind_type,
);
self.create_schedule_with_model(token, channel_id, &schedule)
.await
}
pub async fn create_schedule_with_model(
&self,
token: &Token,
channel_id: &str,
schedule: &Schedule,
) -> Result<Schedule> {
debug!(
"Creating schedule '{}' in channel {}",
schedule.name, channel_id
);
let wrapper = ScheduleWrapper::new(schedule.clone());
let path = format!("/channels/{channel_id}/schedules");
let response = self
.http
.post(token, &path, None::<&()>, Some(&wrapper))
.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> {
let schedule = Schedule::new(
name,
start_timestamp,
end_timestamp,
Some(jump_channel_id.to_string()),
remind_type,
);
self.update_schedule_with_model(token, channel_id, schedule_id, &schedule)
.await
}
pub async fn update_schedule_with_model(
&self,
token: &Token,
channel_id: &str,
schedule_id: &str,
schedule: &Schedule,
) -> Result<Schedule> {
debug!(
"Updating schedule {} in channel {}",
schedule_id, channel_id
);
let wrapper = ScheduleWrapper::new(schedule.clone());
let path = format!("/channels/{channel_id}/schedules/{schedule_id}");
let response = self
.http
.patch(token, &path, None::<&()>, Some(&wrapper))
.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 async fn create_session(
&self,
token: &Token,
identity: &HttpIdentity,
) -> Result<HttpReady> {
debug!("Creating HTTP webhook session");
let response = self
.http
.post(
token,
"/gateway/webhook/sessions",
None::<&()>,
Some(identity),
)
.await?;
Ok(serde_json::from_value(response)?)
}
pub async fn check_sessions(&self, token: &Token) -> Result<Vec<HttpSession>> {
debug!("Checking HTTP webhook sessions");
let mut params = HashMap::new();
params.insert("action", "check");
let response = self
.http
.patch(
token,
"/gateway/webhook/sessions",
Some(¶ms),
None::<&()>,
)
.await?;
Ok(serde_json::from_value(response)?)
}
pub async fn session_list(&self, token: &Token) -> Result<Vec<HttpSession>> {
debug!("Listing HTTP webhook sessions");
let response = self
.http
.get(token, "/gateway/webhook/sessions", None::<&()>)
.await?;
Ok(serde_json::from_value(response)?)
}
pub async fn remove_session(&self, token: &Token, session_id: &str) -> Result<()> {
debug!("Removing HTTP webhook session {}", session_id);
let path = format!("/gateway/webhook/sessions/{session_id}");
self.http.delete(token, &path, None::<&()>).await?;
Ok(())
}
pub fn webhook_validation_response(
request: &WebhookValidationRequest,
signature: impl Into<String>,
data_version: impl Into<String>,
) -> WebhookValidationResponse {
WebhookValidationResponse {
plain_token: request.plain_token.clone(),
signature: signature.into(),
data_version: data_version.into(),
}
}
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());
}
}