Skip to main content

Overview

The Pin command allows moderators to pin or unpin messages in Discord channels. This is a context menu command that toggles the pin state of the selected message.
This command requires semester moderator role or MANAGE_MESSAGES permission.

Permissions Required

To use this command, you must meet one of these requirements:
  • Be registered in the semestermods database table
  • Have the MANAGE_MESSAGES Discord permission

Usage

  1. Right-click on any message in a channel
  2. Select “Apps” → “Toggle Pin State”
  3. The bot will pin the message if it’s unpinned, or unpin it if it’s already pinned

Parameters

message
Message
required
The message to pin or unpin (selected via context menu)

Examples

Pinning a Message

Action: Right-click on an unpinned message and select “Toggle Pin State” Response:
Pinned message
The message will now appear in the channel’s pinned messages.

Unpinning a Message

Action: Right-click on a pinned message and select “Toggle Pin State” Response:
Unpinned message
The message will be removed from the channel’s pinned messages.

Implementation Details

From moderation.rs:35-54:
#[poise::command(
    context_menu_command = "Toggle Pin State",
    check = "has_mod_or_semestermod",
    ephemeral,
    guild_only
)]
pub async fn pin(
    ctx: Context<'_>,
    #[description = "The message to pin"] message: serenity::Message,
) -> Result<(), Error> {
    if message.pinned {
        message
            .unpin(&ctx.serenity_context())
            .await
            .map_err(Error::Serenity)?;
        ctx.say("Unpinned message").await.map_err(Error::Serenity)?;
    } else {
        message
            .pin(&ctx.serenity_context())
            .await
            .map_err(Error::Serenity)?;
        ctx.say("Pinned message").await.map_err(Error::Serenity)?;
    }
    Ok(())
}

Permission Check

The has_mod_or_semestermod function checks:
async fn has_mod_or_semestermod(ctx: Context<'_>) -> Result<bool, Error> {
    let db = &ctx.data().db;
    let member = ctx.author_member().await.unwrap();

    // Check if user is in semestermods table
    let has_perms = sqlx::query("SELECT user_id FROM semestermods WHERE user_id = $1")
        .bind(member.user.id.0 as i64)
        .fetch_optional(db)
        .await
        .map_err(Error::Database)?
        .is_some();

    if has_perms {
        Ok(true)
    } else if member
        .permissions(ctx)
        .map_err(Error::Serenity)?
        .contains(Permissions::MANAGE_MESSAGES)
    {
        return Ok(true);
    } else {
        return Ok(false);
    }
}
The command is ephemeral, meaning the response is only visible to the user who invoked it.

Build docs developers (and LLMs) love