π οΈ Your First Plugin
Welcome! In this guide, we will walk through the process of creating your first Open Ticket plugin.
By the end of this article, you will have a fully functional plugin that listens for ticket creation events and logs messages to the console. You will learn a lot of the basic functionalities of Open Ticket, so be sure to pay attention. Good luck!

Framework Article
Basic Plugins - Your First Plugin
π Prerequisitesβ
Before we start, ensure that you have the following:
- Node.js installed on your system. (Download)
- A code editor, such as Visual Studio Code. (Download)
- A basic understanding of JavaScript/TypeScript and discord.js.
- An existing & working Open Ticket installation
If you haven't already done so, it's recommended to read the π Getting Started guide before starting with this article.
π Setting Up the Pluginβ
Each Open Ticket plugin must be placed inside the ./plugins/
directory. Let's create a folder for our first plugin:
mkdir -p ./plugins/example-plugin
cd ./plugins/example-plugin
plugin.json
β
Now, inside this folder, create a plugin.json
file. This file contains metadata about the plugin. Please insert the following content:
{
"name":"Example Plugin",
"id":"example-plugin",
"version":"1.0.0",
"startFile":"index.ts",
"enabled":false,
"priority":0,
"events":[],
"npmDependencies":[],
"requiredPlugins":[],
"incompatiblePlugins":[],
"details":{
"author":"<your-username>",
"shortDescription":"A simple template for an Open Ticket v4 plugin!",
"longDescription":"A simple example of an Open Ticket v4 plugin!",
"imageUrl":"",
"projectUrl":"",
"tags":["template","example","default"]
}
}
π Understanding plugin.json
β
"name"
: string - The display name of the plugin."id"
: string - The unique identifier of the plugin. This must match the folder name!"version"
: string - The version of the plugin."startFile"
: string - The file which will be executed on startup of the bot."enabled"
: boolean - If set tofalse
, the plugin will be ignored by Open Ticket."priority"
: number - Plugins with a higher priority will be executed first.- Explore Additional Properties...
Now that our plugin is registered, let's create the main script file.
index.ts
β
Create a new file called index.js
inside the ./plugins/example-plugin/
folder. This file will contain our plugin logic. Make sure it already contains the following lines of code:
import {api, opendiscord, utilities} from "#opendiscord"
import * as discord from "discord.js"
if (utilities.project !== "openticket") throw new api.ODPluginError("This plugin only works in Open Ticket!")
declare module "#opendiscord-types" {
//This will be used for autocomplete support while coding
}
π Understanding index.ts
β
Let's break down what weβve added so far:
- Importing dependencies:
api
(#opendiscord
) - Contains all Open Ticket classes, objects, interfaces, variables, ...opendiscord
(#opendiscord
) - This is the Open Ticket bot itself. We will frequently interact with it.utilities
(#opendiscord
) Additional utility functions to make coding easier.discord
(discord.js
) - Allows us to interact with Discordβs API, such as sending messages or modifying channels.
- Ensuring compatibility:
- The
if (utilities.project !== "openticket")
check prevents the plugin from running in unsupported environments or different bots.
- The
- Adding TypeScript autocomplete support:
- The
declare module "#opendiscord-types"
block allows us to extend Open Ticket's type definitions, making development easier with improved autocomplete suggestions.
- The
Now that our setup is complete, let's proceed with implementing the rest of the plugin logic. π
π Writing Code (Step by Step)β
Now that we've set up our plugin directory and index.ts
file, it's time to write the actual code. Below, we'll go through each section carefully, explaining its purpose and functionality.
1οΈβ£ TypeScript Autocomplete Support (Optional)β
This step is optional but highly recommended for smoother development!
declare module "#opendiscord-types" {
export interface ODPluginManagerIds_Default {
"example-plugin": api.ODPlugin
}
export interface ODConfigManagerIds_Default {
"example-plugin:config": api.ODJsonConfig
}
}
πΉ Why declare these types?β
- These extend Open Ticketβs type system, providing autocomplete and type safety while coding.
ODPluginManagerIds_Default
allows our plugin to be recognized under the"example-plugin"
ID.ODConfigManagerIds_Default
allows our plugin configuration to be recognized under the"example-plugin:config"
ID.
2οΈβ£ Registering a Custom Configurationβ
To store plugin settings, we can register a configuration file.
If you want an in-depth guide about this topic, check this article.
opendiscord.events.get("onConfigLoad").listen((configManager) => {
configManager.add(new api.ODJsonConfig("example-plugin:config", "config.json", "./plugins/example-plugin/"))
πΉ What happens here?β
onConfigLoad
is triggered when the bot loads its configurations.configManager.add(...)
registers a new JSON configuration file.ODJsonConfig
is the class responsible for managing & reading the JSON file.- Parameters explained:
"example-plugin:config"
β The unique ID for this config."config.json"
β The filename of the config."./plugins/example-plugin/"
β The directory where the file is stored.
Why store configs inside ./plugins/example-plugin/
instead of ./config/
?
This keeps all plugin-related files self-contained, avoiding clutter in the main config directory.
3οΈβ£ Accessing Configuration Dataβ
Ofcourse we also want to read our plugin settings. This can be done by importing the ODJSonConfig
from opendiscord.configs
.
//you can run this code in any event which has triggered after the config has been loaded!
opendiscord.events.get("someEventYouWantToUseThisConfig").listen(() => {
const ourConfig = opendiscord.configs.get("example-plugin:config")
opendiscord.log("The example config loaded successfully!", "plugin", [
{ key: "var-1", value: ourConfig.data.testVariable1 },
{ key: "var-2", value: ourConfig.data.testVariable2.toString() },
{ key: "var-3", value: ourConfig.data.testVariable3.toString() }
])
})
πΉ What does this do?β
- Retrieve our config file using
opendiscord.configs.get("example-plugin:config")
. - Access the test variables from the config file.
- Uses
opendiscord.log(...)
to print information in the console and logs in a beautiful way.
- Logging configuration values helps ensure that everything loads correctly.
opendiscord.configs
works the same asconfigManager
when we registered the config file.
4οΈβ£ Listening for Ticket Eventsβ
Let's try to react when a user creates a ticket. For this, we can use the same event listeners.
opendiscord.events.get("onTicketCreate").listen((creator) => {
opendiscord.log("Ticket is getting created...", "plugin")
})
opendiscord.events.get("afterTicketCreated").listen((ticket, creator, channel) => {
opendiscord.log("Ticket is ready for usage!", "plugin")
})
πΉ What happens here?β
onTicketCreate
is triggered before a ticket is created (right after the user clicks the button).afterTicketCreated
is fired after the ticket has been created and is assigned to a channel.- Logs
"Ticket is getting created..."
&"Ticket is ready for usage!"
to inform us of the events. - You can use the
ticket
,creator
andchannel
variables to execute your own actions.
βοΈ Testing Your Pluginβ
To test the plugin, make sure the plugin is enabled in plugin.json
and restart your Open Ticket bot:
npm start
If everything is set up correctly, you should see the following messages in your bot console when a ticket is created:
[PLUGIN] Ticket is getting created...
[PLUGIN] Ticket is ready for usage!
π Troubleshootingβ
If you don't see these messages, ensure:
- Your plugin is inside
./plugins/example-plugin/
plugin.json
has"enabled":true
- There are no syntax errors in
index.ts
- The bot has been configured correctly
- The compilation succeeded
- For more advanced testing, check out the Testing Your Plugin guide.
π‘ Exercisesβ
Every article will contain a few Exercise dialogs. Use these to improve your Open Ticket knowledge!
Now it's your turn! Try to listen for a few other events and react to them accordingly.
Exercisesβ
- π’ Listen for ticket deletion.
- π Listen for after a ticket has been closed.
- π΄ Send a message when a ticket is going to get claimed.
π Summaryβ
Congratulations! You've successfully built your first Open Ticket plugin!
What did we build in this tutorial?
- β A plugin with Ensured compatibility with Open Ticket.
- β TypeScript autocomplete support for a smoother coding experience.
- β Registered a custom configuration file for our plugin.
- β
Listened for ticket creation events:
onTicketCreate
β Runs before the ticket is made.afterTicketCreated
β Runs after the ticket is created.
This concludes our first Open Ticket plugin! π In the next steps, weβll explore how to test and refine our plugin.