← back to dnacenta__discord-echo

Function bodies 53 total

All specs Real LLM only Function bodies
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."));
        a
test_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 ›