use serde_json::{json, Value};
use mzrs_proto::{api, realtime as rt};
use mzrs_proto::api::MessageAttachment;
use crate::builders::message::MessageBuilder;
use crate::client::{
DeleteMessageRequest, JoinChatRequest, LeaveChatRequest, MzrsClient, ReactMessageRequest,
SendMessageRequest, UpdateMessageRequest,
};
use crate::error::SdkError;
use crate::upload::AttachmentSource;
const DEFAULT_CHANNEL_TYPE: i32 = 1;
const DEFAULT_MODE: i32 = 2;
const DEFAULT_IS_PUBLIC: bool = true;
#[derive(Clone)]
pub struct ChannelHandle {
client: MzrsClient,
clan_id: String,
channel_id: String,
channel_type: i32,
mode: i32,
is_public: bool,
}
impl ChannelHandle {
pub(crate) fn new(client: MzrsClient, clan_id: String, channel_id: String) -> Self {
Self {
client,
clan_id,
channel_id,
channel_type: DEFAULT_CHANNEL_TYPE,
mode: DEFAULT_MODE,
is_public: DEFAULT_IS_PUBLIC,
}
}
pub fn clan_id(&self) -> &str {
&self.clan_id
}
pub fn id(&self) -> &str {
&self.channel_id
}
pub fn client(&self) -> &MzrsClient {
&self.client
}
pub fn with_channel_type(mut self, channel_type: i32) -> Self {
self.channel_type = channel_type;
self
}
pub fn with_mode(mut self, mode: i32) -> Self {
self.mode = mode;
self
}
pub fn with_public(mut self, is_public: bool) -> Self {
self.is_public = is_public;
self
}
pub fn mode(&self) -> i32 {
self.mode
}
pub fn is_public(&self) -> bool {
self.is_public
}
#[tracing::instrument(skip(self), fields(clan_id = %self.clan_id, channel_id = %self.channel_id))]
pub async fn join(&self) -> Result<rt::Channel, SdkError> {
self.client
.join_chat(JoinChatRequest {
clan_id: self.clan_id.clone(),
channel_id: self.channel_id.clone(),
channel_type: self.channel_type,
is_public: self.is_public,
})
.await
}
#[tracing::instrument(skip(self), fields(clan_id = %self.clan_id, channel_id = %self.channel_id))]
pub async fn leave(&self) -> Result<(), SdkError> {
self.client
.leave_chat(LeaveChatRequest {
clan_id: self.clan_id.clone(),
channel_id: self.channel_id.clone(),
channel_type: self.channel_type,
is_public: self.is_public,
})
.await
}
#[tracing::instrument(skip(self, content))]
pub async fn send_text(
&self,
content: impl Into<String>,
) -> Result<rt::ChannelMessageAck, SdkError> {
self.send_json(json!({ "t": content.into() })).await
}
#[tracing::instrument(skip(self, content))]
pub async fn send_json(&self, content: Value) -> Result<rt::ChannelMessageAck, SdkError> {
self.client
.send_message(SendMessageRequest {
clan_id: self.clan_id.clone(),
channel_id: self.channel_id.clone(),
mode: self.mode,
is_public: self.is_public,
content,
mentions: vec![],
attachments: vec![],
references: vec![],
anonymous_message: false,
mention_everyone: false,
avatar: None,
code: 0,
topic_id: None,
id: None,
})
.await
}
#[tracing::instrument(skip(self, content))]
pub async fn send_content(
&self,
content: crate::builders::message::MessageContent,
) -> Result<rt::ChannelMessageAck, SdkError> {
let prepared = content.build_prepared();
let json_value: Value = serde_json::from_str(&prepared.json).unwrap_or(Value::Null);
self.client
.send_message(SendMessageRequest {
clan_id: self.clan_id.clone(),
channel_id: self.channel_id.clone(),
mode: self.mode,
is_public: self.is_public,
content: json_value,
mentions: prepared.mentions,
attachments: vec![],
references: vec![],
anonymous_message: false,
mention_everyone: false,
avatar: None,
code: 0,
topic_id: None,
id: None,
})
.await
}
#[tracing::instrument(skip(self, content))]
pub async fn update_json(
&self,
message_id: &str,
content: Value,
) -> Result<rt::ChannelMessageAck, SdkError> {
self.client
.update_message(UpdateMessageRequest {
clan_id: self.clan_id.clone(),
channel_id: self.channel_id.clone(),
message_id: message_id.to_string(),
mode: self.mode,
is_public: self.is_public,
content,
mentions: vec![],
attachments: vec![],
hide_editted: true,
topic_id: None,
is_update_msg_topic: false,
})
.await
}
#[tracing::instrument(skip(self))]
pub async fn delete(&self, message_id: &str) -> Result<rt::ChannelMessageAck, SdkError> {
self.client
.delete_message(DeleteMessageRequest {
clan_id: self.clan_id.clone(),
channel_id: self.channel_id.clone(),
message_id: message_id.to_string(),
mode: self.mode,
is_public: self.is_public,
has_attachment: false,
topic_id: None,
mentions: vec![],
references: vec![],
})
.await
}
#[tracing::instrument(skip(self, emoji))]
pub async fn react(
&self,
message_id: &str,
emoji_id: &str,
emoji: impl Into<String>,
message_sender_id: &str,
action_delete: bool,
) -> Result<api::MessageReaction, SdkError> {
self.client
.react_message(ReactMessageRequest {
id: None,
clan_id: self.clan_id.clone(),
channel_id: self.channel_id.clone(),
mode: self.mode,
is_public: self.is_public,
message_id: message_id.to_string(),
emoji_id: emoji_id.to_string(),
emoji: emoji.into(),
count: 1,
message_sender_id: message_sender_id.to_string(),
sender_id: None,
sender_name: None,
sender_avatar: None,
action_delete,
topic_id: None,
emoji_recent_id: None,
})
.await
}
#[tracing::instrument(skip(self, avatar))]
pub async fn update_avatar(&self, avatar: Option<String>) -> Result<(), SdkError> {
self.client
.update_channel_avatar(&self.clan_id, &self.channel_id, avatar)
.await
}
#[deprecated(
note = "Mezon bot attachment upload is not supported; attach externally hosted URLs instead"
)]
#[tracing::instrument(skip(self, _source, _filename, _filetype))]
pub async fn upload_attachment(
&self,
_source: impl Into<AttachmentSource>,
_filename: impl Into<String>,
_filetype: impl Into<String>,
) -> Result<MessageAttachment, SdkError> {
Err(SdkError::Core(mzrs_core::CoreError::Unsupported(
"Mezon does not support attachment upload for bots".to_string(),
)))
}
pub fn message(&self) -> MessageBuilder {
MessageBuilder::new(self.clone())
}
}