> For the complete documentation index, see [llms.txt](https://docs.ragnasync.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.ragnasync.com/ragnascript/custom-messages.md).

# NPC Examples

Ready-to-use npc\_message scripts for Discord announcements

Ready-to-use `npc_message` scripts for server-generated announcements. Replace `YOUR_CHANNEL_ID` with your Discord channel ID.

The `npc_message` action is for messages your server sends on its own — events, maintenance notices, WoE status, staff alerts. It only needs `name`, `message`, and `channel`. (For player chat use [Message Sync](/getting-started/channel-messages.md); for staff `@request` alerts see [Ticket Support Command](/features/ticket-system/ticket-support.md).)

***

## It's just a script function

`ragnascript()` is a normal rAthena/Hercules script function — exactly like `announce`, `mes`, or `getitem`. That means it drops straight into **any script your server already runs**: NPCs, event labels (`OnPCKillEvent`, `OnAgitStart`, …), quest scripts, instances, functions, item bonus scripts, `@command` handlers — anywhere a script command is valid.

There's **no limit on what you can announce**. If rAthena/Hercules can script it, you can post it to Discord. The examples below are common starting points, not the boundary — wherever you can already write a line of script, you can add a `ragnascript()` call beside it. It's plug-and-play: paste the function in, set your channel, done.

{% hint style="info" %}
Anything you can detect in script — a player hitting max level, a custom currency changing, an MVP spawning, a GM toggling an event — can trigger a Discord announcement. Just put the `ragnascript()` call wherever that logic already lives.
{% endhint %}

***

## How npc\_message works

Every `npc_message` call has the same three fields. That's the whole mechanic:

```c
ragnascript("[action: npc_message][name: Town Crier][message: The shop is now open!][channel: YOUR_CHANNEL_ID]");
```

The syntax is always `[action: npc_message]` followed by the three fields, in any order:

```
ragnascript("[action: npc_message][name: <author>][message: <text>][channel: <id>]");
```

| Field     | Required | What it does                                                                                                                                                   |
| --------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `action`  | Yes      | Always `npc_message` for these scripts. Tells RagnaSync this is an unattributed server announcement.                                                           |
| `name`    | Yes      | The author name shown in Discord. This is **not** a real player — it's whatever label you want the message to come from (`System`, `WoE`, `Event Manager`, …). |
| `message` | Yes      | The text that appears in Discord. Plain text, and supports Discord emoji shortcodes + Markdown. Build it however you like — see below.                         |
| `channel` | Yes      | The Discord channel ID the message is posted to. Different calls can target different channels.                                                                |

No `account_id` or `char_id` is needed — `npc_message` is unattributed, so it never shows a player avatar or links to a character. That's exactly why it's the right action for anything the *server* says.

### Choosing when it fires

The `ragnascript()` call is identical everywhere — what changes between implementations is the rAthena **event label** that triggers it. Pick the label that matches when you want the announcement to fire:

| Trigger label                         | Fires when                                      | Example below                                                                                  |
| ------------------------------------- | ----------------------------------------------- | ---------------------------------------------------------------------------------------------- |
| `OnInit`                              | Once, when scripts finish loading (server boot) | [Server Online Notice](#server-online-notice)                                                  |
| `OnHour00`–`OnHour23` / `OnClockHHMM` | At a fixed time of day                          | [Event Announcements](#event-announcements), [Scheduled Server Stats](#scheduled-server-stats) |
| `OnMinuteNN`                          | Every hour at minute NN                         | [Server Maintenance Notice](#server-maintenance-notice)                                        |
| `OnAgitStart` / `OnAgitEnd`           | WoE begins / ends                               | [WoE Status](#woe-status)                                                                      |
| `OnNPCKillEvent`                      | A monster dies (use `killedrid`)                | [MVP Kill Announcements](#mvp-kill-announcements)                                              |
| `OnPCKillEvent`                       | A player kills another player/monster           | [Anti-Bot Alerts](#anti-bot-alerts)                                                            |
| `function` / `callfunc`               | On demand, called from your other scripts       | [Anti-Bot Alerts](#anti-bot-alerts) (`ReportBot`)                                              |

These are standard rAthena/Hercules labels — RagnaSync doesn't add any of its own. Any event that can run script code can call `ragnascript()`.

### Building the message dynamically

`message` is just a string, so you can concatenate game data into it with `+`, the same as any rAthena script. Common sources for announcement text:

| Function                           | Returns                          | Used in                                                           |
| ---------------------------------- | -------------------------------- | ----------------------------------------------------------------- |
| `strcharinfo(0)`                   | The triggering player's name     | [MVP Kill](#mvp-kill-announcements), [Anti-Bot](#anti-bot-alerts) |
| `strcharinfo(3)`                   | The player's current map name    | [MVP Kill](#mvp-kill-announcements)                               |
| `strmobinfo(1, <mobID>)`           | A monster's display name         | [MVP Kill](#mvp-kill-announcements)                               |
| `getmonsterinfo(<id>, MOB_MVPEXP)` | `>0` for MVP mobs (filter kills) | [MVP Kill](#mvp-kill-announcements)                               |
| `getusers(1)`                      | Online player count              | [Scheduled Server Stats](#scheduled-server-stats)                 |
| `gettime(DT_HOUR)`                 | Current server hour (0–23)       | [Server Maintenance](#server-maintenance-notice)                  |

Here's the pattern — pull a value, drop it into the message:

```c
-	script	BossWatch	-1,{
OnNPCKillEvent:
    // killedrid holds the mob ID that just died
    if (killedrid == 1086) {   // Golden Thief Bug
        ragnascript("[action: npc_message][name: Boss Watch][message: " + strcharinfo(0) + " just slayed " + strmobinfo(1, killedrid) + "!][channel: YOUR_CHANNEL_ID]");
    }
    end;
}
```

{% hint style="info" %}
Even though you can reference the player who triggered the event (like `strcharinfo(0)` above), the message is still posted as the **NPC name** you set — not as that player. Use `npc_message` when you want the *announcement* in Discord, not a player-attributed chat line.
{% endhint %}

***

## Event Announcements

Announce events to Discord automatically:

```c
-	script	EventAnnouncer	-1,{
OnHour12:
    ragnascript("[action: npc_message][name: Event Manager][message: Daily Hunt is now active!][channel: YOUR_CHANNEL_ID]");
    announce "Daily Hunt event is now active!", bc_all;
    end;
}
```

***

## Server Maintenance Notice

```c
-	script	Maintenance	-1,{
OnMinute30:
    if (gettime(DT_HOUR) == 5) {
        ragnascript("[action: npc_message][name: System][message: Server maintenance in 30 minutes.][channel: YOUR_CHANNEL_ID]");
    }
    end;
}
```

***

## WoE Status

```c
-	script	WoEAnnounce	-1,{
OnAgitStart:
    ragnascript("[action: npc_message][name: WoE][message: War of Emperium has begun!][channel: YOUR_CHANNEL_ID]");
    end;

OnAgitEnd:
    ragnascript("[action: npc_message][name: WoE][message: War of Emperium has ended!][channel: YOUR_CHANNEL_ID]");
    end;
}
```

***

## Anti-Bot Alerts

Notify staff when suspicious activity is detected:

```c
-	script	AntiBotAlert	-1,{
OnPCKillEvent:
    // Detect unusually fast kill rate
    if (@kill_count == 0) {
        @first_kill = gettimetick(2);
    }
    @kill_count++;

    .@elapsed = gettimetick(2) - @first_kill;
    if (@kill_count >= 50 && .@elapsed < 60) {
        ragnascript("[action: npc_message][name: Anti-Bot][message: Suspicious activity: " + strcharinfo(0) + " killed 50+ mobs in under 1 minute][channel: YOUR_CHANNEL_ID]");
        @kill_count = 0;
    }

    // Reset counter every 5 minutes
    if (.@elapsed > 300) {
        @kill_count = 0;
    }
    end;
}
```

You can also trigger alerts from your existing anti-bot systems:

```c
// Call this function when bot behavior is detected
function	script	ReportBot	{
    .@char_name$ = getarg(0);
    .@reason$ = getarg(1);
    ragnascript("[action: npc_message][name: Anti-Bot][message: " + .@char_name$ + " flagged - " + .@reason$ + "][channel: YOUR_CHANNEL_ID]");
    return;
}

// Usage from other scripts:
// callfunc("ReportBot", strcharinfo(0), "Teleport hack detected");
```

***

## Server Online Notice

Post to Discord the moment the server finishes loading — `OnInit` runs once when scripts load:

```c
-	script	ServerStatus	-1,{
OnInit:
    ragnascript("[action: npc_message][name: Server][message: :green_circle: Server is now online and accepting connections!][channel: YOUR_CHANNEL_ID]");
    end;
}
```

{% hint style="info" %}
Discord renders emoji shortcodes like `:green_circle:` and standard Markdown (`**bold**`, `*italic*`) inside the `message` text — handy for making announcements stand out.

```c
ragnascript("[action: npc_message][name: Event][message: **2x EXP** is now live for the next hour!][channel: YOUR_CHANNEL_ID]");
```

{% endhint %}

***

## MVP Kill Announcements

Announce who landed an MVP, with the boss name pulled straight from the mob database:

```c
-	script	MvpAnnouncer	-1,{
OnNPCKillEvent:
    // Only announce actual MVP mobs
    if (getmonsterinfo(killedrid, MOB_MVPEXP) > 0) {
        ragnascript("[action: npc_message][name: MVP Tracker][message: :crown: " + strcharinfo(0) + " defeated " + strmobinfo(1, killedrid) + " in " + strcharinfo(3) + "!][channel: YOUR_CHANNEL_ID]");
    }
    end;
}
```

***

## Routing to Different Channels

The `channel` field is just a variable — store your channel IDs once and route each announcement where it belongs:

```c
-	script	AnnounceRouter	-1,{
OnInit:
    // Set up your channels in one place
    .events$  = "1111111111111111111";
    .staff$   = "2222222222222222222";
    .woe$     = "3333333333333333333";
    end;

OnClock1900:
    // Goes to the events channel
    ragnascript("[action: npc_message][name: Event][message: Treasure Hunt starts in 10 minutes!][channel: " + .events$ + "]");
    end;

OnAgitStart:
    // Goes to the WoE channel
    ragnascript("[action: npc_message][name: WoE][message: The castles are now open — fight!][channel: " + .woe$ + "]");
    end;
}
```

***

## Scheduled Server Stats

Build the message from live game data and post it on a timer. Here we report the online player count every hour:

```c
-	script	PlayerCount	-1,{
OnHour00:
OnHour06:
OnHour12:
OnHour18:
    .@online = getusers(1);
    ragnascript("[action: npc_message][name: Server Stats][message: :busts_in_silhouette: There are currently **" + .@online + "** players online!][channel: YOUR_CHANNEL_ID]");
    end;
}
```

***

## Tips

**Keep it to announcements** - `npc_message` only requires `name`, `message`, and `channel`. There's no player attached, so use it for anything your server says on its own.

**Mind the brackets** - RagnaSync parses fields with `[key: value]`, so avoid putting literal `[` or `]` inside your `message` text — they can confuse the parser. Stick to plain text, emoji, and Markdown.

**One line per call** - Keep each `ragnascript("...")` on a single line, even when concatenating with `+`. Build longer text in a `.@msg$` variable first if it gets unwieldy, then pass `.@msg$` as the message.

**Rate limiting** - For scripts that fire on frequent events (like `OnPCKillEvent` above), add a cooldown so you don't flood the channel:

```c
if (gettimetick(2) - @last_msg < 60) {
    end;
}
@last_msg = gettimetick(2);
```

***

Need the basics? See [RagnaScript Overview](/ragnascript/scripts.md).
