Function bodies 53 total
new function · rust · L16-L22 (7 LOC)src/client.rs
pub fn new(bot_token: String, channel_map: HashMap<String, String>) -> Arc<Self> {
Arc::new(Self {
http: reqwest::Client::new(),
bot_token,
channel_map,
})
}send_message function · rust · L25-L31 (7 LOC)src/client.rs
pub async fn send_message(&self, channel_name: &str, content: &str) -> Result<(), ClientError> {
let channel_id = self
.channel_map
.get(channel_name)
.ok_or_else(|| ClientError::UnknownChannel(channel_name.to_string()))?;
self.send_message_by_id(channel_id, content).await
}send_message_by_id function · rust · L34-L58 (25 LOC)src/client.rs
pub async fn send_message_by_id(
&self,
channel_id: &str,
content: &str,
) -> Result<(), ClientError> {
let chunks = split_message(content);
for chunk in chunks {
let url = format!("{}/channels/{}/messages", DISCORD_API_BASE, channel_id);
let resp = self
.http
.post(&url)
.header("Authorization", format!("Bot {}", self.bot_token))
.json(&serde_json::json!({ "content": chunk }))
.send()
.await
.map_err(|e| ClientError::Http(e.to_string()))?;
if !resp.status().is_success() {
let status = resp.status().as_u16();
let body = resp.text().await.unwrap_or_default();
return Err(ClientError::Api { status, body });
}
}
Ok(())
}resolve_channel function · rust · L61-L63 (3 LOC)src/client.rs
pub fn resolve_channel(&self, name: &str) -> Option<&str> {
self.channel_map.get(name).map(|s| s.as_str())
}channel_names function · rust · L66-L68 (3 LOC)src/client.rs
pub fn channel_names(&self) -> Vec<&str> {
self.channel_map.keys().map(|k| k.as_str()).collect()
}split_message function · rust · L72-L98 (27 LOC)src/client.rs
pub fn split_message(text: &str) -> Vec<&str> {
if text.len() <= DISCORD_MAX_LEN {
return vec![text];
}
let mut chunks = Vec::new();
let mut remaining = text;
while !remaining.is_empty() {
if remaining.len() <= DISCORD_MAX_LEN {
chunks.push(remaining);
break;
}
// Find the best split point: last newline before the limit
let search_range = &remaining[..DISCORD_MAX_LEN];
let split_at = search_range
.rfind('\n')
.map(|pos| pos + 1) // include the newline in the first chunk
.unwrap_or(DISCORD_MAX_LEN); // hard split if no newline
chunks.push(&remaining[..split_at]);
remaining = &remaining[split_at..];
}
chunks
}fmt function · rust · L108-L116 (9 LOC)src/client.rs
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
ClientError::UnknownChannel(name) => write!(f, "Unknown channel: {}", name),
ClientError::Http(msg) => write!(f, "HTTP error: {}", msg),
ClientError::Api { status, body } => {
write!(f, "Discord API error {}: {}", status, body)
}
}
}Repobility · code-quality intelligence platform · https://repobility.com
test_split_short_message function · rust · L126-L129 (4 LOC)src/client.rs
fn test_split_short_message() {
let chunks = split_message("hello");
assert_eq!(chunks, vec!["hello"]);
}test_split_at_newline function · rust · L132-L147 (16 LOC)src/client.rs
fn test_split_at_newline() {
let mut msg = String::new();
// Build a message with lines that push past 2000 chars
for i in 0..30 {
msg.push_str(&format!("Line {} with some content here\n", i));
}
// Ensure it's over 2000
while msg.len() <= DISCORD_MAX_LEN {
msg.push_str("padding line\n");
}
let chunks = split_message(&msg);
assert!(chunks.len() >= 2);
// First chunk should end at a newline
assert!(chunks[0].ends_with('\n'));
assert!(chunks[0].len() <= DISCORD_MAX_LEN);
}test_split_no_newlines function · rust · L150-L156 (7 LOC)src/client.rs
fn test_split_no_newlines() {
let msg = "x".repeat(DISCORD_MAX_LEN + 500);
let chunks = split_message(&msg);
assert_eq!(chunks.len(), 2);
assert_eq!(chunks[0].len(), DISCORD_MAX_LEN);
assert_eq!(chunks[1].len(), 500);
}test_split_exact_boundary function · rust · L159-L163 (5 LOC)src/client.rs
fn test_split_exact_boundary() {
let msg = "x".repeat(DISCORD_MAX_LEN);
let chunks = split_message(&msg);
assert_eq!(chunks.len(), 1);
}test_resolve_channel function · rust · L166-L173 (8 LOC)src/client.rs
fn test_resolve_channel() {
let client = DiscordClient::new(
"token".to_string(),
HashMap::from([("test".to_string(), "123".to_string())]),
);
assert_eq!(client.resolve_channel("test"), Some("123"));
assert_eq!(client.resolve_channel("nope"), None);
}default_chat_channel function · rust · L25-L28 (4 LOC)src/config.rs
fn default_chat_channel() -> String {
"discord".to_string()
}channel_id function · rust · L32-L34 (3 LOC)src/config.rs
pub fn channel_id(&self, name: &str) -> Option<&str> {
self.channels.get(name).map(|s| s.as_str())
}channel_name function · rust · L37-L42 (6 LOC)src/config.rs
pub fn channel_name(&self, id: &str) -> Option<&str> {
self.channels
.iter()
.find(|(_, v)| v.as_str() == id)
.map(|(k, _)| k.as_str())
}Source: Repobility analyzer · https://repobility.com
is_listen_channel function · rust · L45-L54 (10 LOC)src/config.rs
pub fn is_listen_channel(&self, channel_id: &str) -> bool {
if self.listen_channels.is_empty() {
return true;
}
let name = self.channel_name(channel_id);
match name {
Some(n) => self.listen_channels.iter().any(|l| l == n),
None => false,
}
}is_allowed_user function · rust · L57-L62 (6 LOC)src/config.rs
pub fn is_allowed_user(&self, user_id: &str) -> bool {
if self.allowed_user_ids.is_empty() {
return true;
}
self.allowed_user_ids.iter().any(|id| id == user_id)
}test_config function · rust · L68-L83 (16 LOC)src/config.rs
fn test_config() -> Config {
Config {
bot_token: "test-token".to_string(),
guild_id: "123".to_string(),
listen_channels: vec!["constellation".to_string()],
allowed_user_ids: vec![],
chat_endpoint: default_chat_endpoint(),
chat_secret: None,
chat_channel_name: "discord".to_string(),
channels: HashMap::from([
("constellation".to_string(), "111".to_string()),
("notes".to_string(), "222".to_string()),
]),
}
}test_channel_id_lookup function · rust · L86-L90 (5 LOC)src/config.rs
fn test_channel_id_lookup() {
let config = test_config();
assert_eq!(config.channel_id("constellation"), Some("111"));
assert_eq!(config.channel_id("unknown"), None);
}test_channel_name_lookup function · rust · L93-L97 (5 LOC)src/config.rs
fn test_channel_name_lookup() {
let config = test_config();
assert_eq!(config.channel_name("111"), Some("constellation"));
assert_eq!(config.channel_name("999"), None);
}test_is_listen_channel function · rust · L100-L105 (6 LOC)src/config.rs
fn test_is_listen_channel() {
let config = test_config();
assert!(config.is_listen_channel("111")); // constellation
assert!(!config.is_listen_channel("222")); // notes not in listen list
assert!(!config.is_listen_channel("999")); // unknown
}test_empty_listen_allows_all function · rust · L108-L113 (6 LOC)src/config.rs
fn test_empty_listen_allows_all() {
let mut config = test_config();
config.listen_channels.clear();
assert!(config.is_listen_channel("111"));
assert!(config.is_listen_channel("222"));
}test_allowed_users_empty_allows_all function · rust · L116-L119 (4 LOC)src/config.rs
fn test_allowed_users_empty_allows_all() {
let config = test_config();
assert!(config.is_allowed_user("anyone"));
}Same scanner, your repo: https://repobility.com — Repobility
test_allowed_users_filter function · rust · L122-L127 (6 LOC)src/config.rs
fn test_allowed_users_filter() {
let mut config = test_config();
config.allowed_user_ids = vec!["user1".to_string()];
assert!(config.is_allowed_user("user1"));
assert!(!config.is_allowed_user("user2"));
}run_gateway function · rust · L19-L292 (274 LOC)src/gateway.rs
pub async fn run_gateway(
config: Arc<Config>,
message_tx: mpsc::Sender<IncomingMessage>,
shutdown: Arc<Notify>,
) {
let mut session_id: Option<String> = None;
let mut resume_url: Option<String> = None;
let mut self_bot_id: Option<String> = None;
let sequence = Arc::new(AtomicU64::new(0));
let mut backoff_secs = 1u64;
loop {
let url = resume_url.as_deref().unwrap_or(GATEWAY_URL).to_string();
tracing::info!("Connecting to Discord gateway: {}", url);
let ws = match connect_async(&url).await {
Ok((stream, _)) => {
backoff_secs = 1;
stream
}
Err(e) => {
tracing::error!("Gateway connect failed: {e}");
let delay = Duration::from_secs(backoff_secs);
tokio::select! {
_ = time::sleep(delay) => {}
_ = shutdown.notified() => return,
}
backoff_secs = new function · rust · L34-L44 (11 LOC)src/lib.rs
pub fn new(config: Config) -> Self {
let client = DiscordClient::new(config.bot_token.clone(), config.channels.clone());
let config = Arc::new(config);
Self {
config,
client,
shutdown: Arc::new(Notify::new()),
gateway_handle: None,
forwarder_handle: None,
}
}client function · rust · L47-L49 (3 LOC)src/lib.rs
pub fn client(&self) -> Arc<DiscordClient> {
Arc::clone(&self.client)
}health_check function · rust · L52-L58 (7 LOC)src/lib.rs
fn health_check(&self) -> HealthStatus {
if self.gateway_handle.is_some() {
HealthStatus::Healthy
} else {
HealthStatus::Down("Not started".to_string())
}
}get_setup_prompts function · rust · L61-L78 (18 LOC)src/lib.rs
fn get_setup_prompts() -> Vec<SetupPrompt> {
vec![
SetupPrompt {
key: "bot_token".to_string(),
question: "Discord bot token:".to_string(),
default: None,
required: true,
secret: true,
},
SetupPrompt {
key: "guild_id".to_string(),
question: "Discord server (guild) ID:".to_string(),
default: None,
required: true,
secret: false,
},
]
}create function · rust · L82-L88 (7 LOC)src/lib.rs
pub async fn create(
config: &serde_json::Value,
_ctx: &PluginContext,
) -> Result<Box<dyn Plugin>, Box<dyn std::error::Error + Send + Sync>> {
let cfg: Config = serde_json::from_value(config.clone())?;
Ok(Box::new(DiscordEcho::new(cfg)))
}meta function · rust · L91-L97 (7 LOC)src/lib.rs
fn meta(&self) -> PluginMeta {
PluginMeta {
name: "discord-echo".into(),
version: env!("CARGO_PKG_VERSION").into(),
description: "Discord text integration".into(),
}
}Repobility · MCP-ready · https://repobility.com
role function · rust · L98-L101 (4 LOC)src/lib.rs
fn role(&self) -> PluginRole {
PluginRole::Interface
}start function · rust · L102-L127 (26 LOC)src/lib.rs
fn start(&mut self) -> PluginResult<'_> {
Box::pin(async move {
if self.gateway_handle.is_some() {
return Err("Already running".into());
}
let (message_tx, message_rx) = mpsc::channel::<IncomingMessage>(64);
let gw_config = Arc::clone(&self.config);
let gw_shutdown = Arc::clone(&self.shutdown);
self.gateway_handle = Some(tokio::spawn(async move {
gateway::run_gateway(gw_config, message_tx, gw_shutdown).await;
}));
let fwd_client = Arc::clone(&self.client);
let fwd_config = Arc::clone(&self.config);
let fwd_shutdown = Arc::clone(&self.shutdown);
self.forwarder_handle = Some(tokio::spawn(async move {
message_forwarder(message_rx, fwd_client, fwd_config, fwd_shutdown).await;
}));
tracing::info!("Discord text integration started");
Ok(())
})
}stop function · rust · L128-L145 (18 LOC)src/lib.rs
fn stop(&mut self) -> PluginResult<'_> {
Box::pin(async move {
self.shutdown.notify_waiters();
if let Some(h) = self.gateway_handle.take() {
let _ = tokio::time::timeout(std::time::Duration::from_secs(5), h).await;
}
if let Some(h) = self.forwarder_handle.take() {
let _ = tokio::time::timeout(std::time::Duration::from_secs(5), h).await;
}
self.shutdown = Arc::new(Notify::new());
tracing::info!("Discord text integration stopped");
Ok(())
})
}health function · rust · L146-L149 (4 LOC)src/lib.rs
fn health(&self) -> Pin<Box<dyn Future<Output = HealthStatus> + Send + '_>> {
Box::pin(async move { self.health_check() })
}setup_prompts function · rust · L150-L153 (4 LOC)src/lib.rs
fn setup_prompts(&self) -> Vec<SetupPrompt> {
Self::get_setup_prompts()
}as_any function · rust · L154-L157 (4 LOC)src/lib.rs
fn as_any(&self) -> &dyn Any {
self
}is_silent function · rust · L166-L171 (6 LOC)src/lib.rs
fn is_silent(response: &str) -> bool {
let trimmed = response.trim();
SILENT_MARKERS
.iter()
.any(|marker| trimmed.starts_with(marker))
}message_forwarder function · rust · L175-L247 (73 LOC)src/lib.rs
async fn message_forwarder(
mut rx: mpsc::Receiver<IncomingMessage>,
client: Arc<DiscordClient>,
config: Arc<Config>,
shutdown: Arc<Notify>,
) {
let http = reqwest::Client::new();
loop {
tokio::select! {
msg = rx.recv() => {
let msg = match msg {
Some(m) => m,
None => return, // gateway dropped
};
let channel_label = msg.channel_name.as_deref().unwrap_or("discord");
tracing::info!(
"Message from {} in #{}: {}",
msg.author_name,
channel_label,
if msg.content.len() > 80 { &msg.content[..80] } else { &msg.content }
);
// Forward to chat endpoint
let mut req = http
.post(&config.chat_endpoint)
.json(&serde_json::json!({
"message": msg.content,
Repobility · code-quality intelligence platform · https://repobility.com
test_health_down_before_start function · rust · L255-L288 (34 LOC)src/lib.rs
async fn test_health_down_before_start() {
let config = Config {
bot_token: "test".to_string(),
guild_id: "123".to_string(),
listen_channels: vec![],
allowed_user_ids: vec![],
chat_endpoint: "http://localhost:3100/chat".to_string(),
chat_secret: None,
chat_channel_name: "discord".to_string(),
channels: HashMap::new(),
};
let echo = DiscordEcho::new(config);
let health = Plugin::health(&echo).await;
assert!(matches!(health, HealthStatus::Down(_)));
}
#[test]
fn test_setup_prompts_not_empty() {
let config = Config {
bot_token: "test".to_string(),
guild_id: "123".to_string(),
listen_channels: vec![],
allowed_user_ids: vec![],
chat_endpoint: "http://localhost:3100/chat".to_string(),
chat_secret: None,
chat_channel_name: "discord".to_string(),
test_setup_prompts_not_empty function · rust · L272-L318 (47 LOC)src/lib.rs
fn test_setup_prompts_not_empty() {
let config = Config {
bot_token: "test".to_string(),
guild_id: "123".to_string(),
listen_channels: vec![],
allowed_user_ids: vec![],
chat_endpoint: "http://localhost:3100/chat".to_string(),
chat_secret: None,
chat_channel_name: "discord".to_string(),
channels: HashMap::new(),
};
let echo = DiscordEcho::new(config);
let prompts = Plugin::setup_prompts(&echo);
assert!(!prompts.is_empty());
assert!(prompts.iter().any(|p| p.key == "bot_token"));
assert!(prompts.iter().any(|p| p.key == "guild_id"));
}
#[test]
fn test_is_silent() {
assert!(is_silent("[SILENT]"));
assert!(is_silent("[SILENT] I have nothing to add"));
assert!(is_silent("[NO_RESPONSE]"));
assert!(is_silent("No response requested"));
assert!(is_silent("No response requested."));
atest_is_silent function · rust · L291-L301 (11 LOC)src/lib.rs
fn test_is_silent() {
assert!(is_silent("[SILENT]"));
assert!(is_silent("[SILENT] I have nothing to add"));
assert!(is_silent("[NO_RESPONSE]"));
assert!(is_silent("No response requested"));
assert!(is_silent("No response requested."));
assert!(is_silent(" [SILENT] ")); // trimmed
assert!(!is_silent("Hello, how are you?"));
assert!(!is_silent(""));
assert!(!is_silent("I think [SILENT] is interesting")); // not at start
}new function · rust · L13-L15 (3 LOC)src/tool.rs
pub fn new(client: Arc<DiscordClient>) -> Self {
Self { client }
}name function · rust · L16-L19 (4 LOC)src/tool.rs
pub fn name() -> &'static str {
"discord_post"
}description function · rust · L20-L23 (4 LOC)src/tool.rs
pub fn description() -> &'static str {
"Post a message to a Discord channel. Use channel names from your config (e.g. 'constellation', 'self-evolution'), not raw IDs."
}input_schema function · rust · L24-L40 (17 LOC)src/tool.rs
pub fn input_schema() -> serde_json::Value {
serde_json::json!({
"type": "object",
"properties": {
"channel": {
"type": "string",
"description": "Channel name (e.g. 'constellation', 'notes')"
},
"message": {
"type": "string",
"description": "Message content to post"
}
},
"required": ["channel", "message"]
})
}execute function · rust · L41-L59 (19 LOC)src/tool.rs
pub async fn execute(&self, input: serde_json::Value) -> Result<String, String> {
let channel = input["channel"]
.as_str()
.ok_or_else(|| "Missing 'channel' parameter".to_string())?;
let message = input["message"]
.as_str()
.ok_or_else(|| "Missing 'message' parameter".to_string())?;
if message.is_empty() {
return Err("Message cannot be empty".to_string());
}
self.client
.send_message(channel, message)
.await
.map(|_| format!("Message posted to #{}", channel))
.map_err(|e| format!("Failed to post to #{}: {}", channel, e))
}Source: Repobility analyzer · https://repobility.com
available_channels function · rust · L62-L64 (3 LOC)src/tool.rs
pub fn available_channels(&self) -> Vec<&str> {
self.client.channel_names()
}test_tool_metadata function · rust · L72-L81 (10 LOC)src/tool.rs
fn test_tool_metadata() {
assert_eq!(DiscordPostTool::name(), "discord_post");
assert!(!DiscordPostTool::description().is_empty());
let schema = DiscordPostTool::input_schema();
assert_eq!(schema["type"], "object");
let required = schema["required"].as_array().unwrap();
assert!(required.contains(&serde_json::json!("channel")));
assert!(required.contains(&serde_json::json!("message")));
}test_gateway_payload_serialize function · rust · L82-L90 (9 LOC)src/types.rs
fn test_gateway_payload_serialize() {
let payload = GatewayPayload {
op: OP_HEARTBEAT,
d: serde_json::json!(42),
};
let json = serde_json::to_string(&payload).unwrap();
assert!(json.contains("\"op\":1"));
assert!(json.contains("\"d\":42"));
}page 1 / 2next ›