Plugins in CHIM allow you to integrate CHIM with other mods.
For example you could add an AI integration with a camping mod to make an AI NPC build a campsite by saying “Hey Lydia, build us a camp”.
If you are a mod developer you can make your own plugin quite easily!
How the Plugin Installer Works
Packaging Your Plugin
{
"name":"twitch-bot",
"description":"Allows viewers to control AI NPC's via Twitch chat.",
"config_url":"/HerikaServer/ext/twitch-bot/index.php",
"config_url_target": "_blank",
"git_repo":"RANGROO/CHIM-Twitch-Bot",
"version": "1.1.1",
"schema_version":2
}
2. Optional: Add /migrations folder with SQL files (001_create_table.sql)
1. Create a 'migrations' directory in your plugin's root folder:
my_plugin/
├── migrations/
├── manifest.json
└── other files...
2. Add SQL migration files in the migrations directory:
- Name files with a numeric prefix for ordering
- Use .sql extension
- Example names:
001_initial_schema.sql
002_add_indexes.sql
003_add_new_feature.sql
3. Write your SQL migrations:
-- Example migration file (001_initial_schema.sql):
CREATE TABLE IF NOT EXISTS my_plugin_data (
id SERIAL PRIMARY KEY,
name VARCHAR(255) NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
3. Create a GitHub release with tag (e.g., "1.0.0")
4. Setup your latest release into the format: [plugin-name].tar.gz (e.g., my-plugin.tar.gz)
Plugin Repository Format
Submit a PR into the /aiagent repository and edit this file ui/data/plugin_repository.json
{
"plugins": {
"plugin-name": {
"name": "plugin-name",
"description": "Short description",
"git_repo": "Username/Repository",
"github_url": "https://github.com/Username/Repository"
}
/***
Example of plugin.
External/3rd party functions should start with prefix ExtCmd or WebCmd
ExtCdm is for functions whose return value is provided by a Papyrus plugin.
WebCmd is for functions which return value is provided by server plugin itself
In this case, function code name will be ExtCmdHeal because we need a papyrus script to do the actual command and reports back result.
For Papyrus plugin developers:
Just need to bind to SPG_CommandReceived event.
Event OnInit()
MiscUtil.PrintConsole("[HerikaAI ext plugin heal] activated")
RegisterForModEvent("CHIM_CommandReceived", "MyCommand")
EndEvent
Event MyCommand(String npcName, String command, String parameter)
MiscUtil.PrintConsole("[AIFF] External command "+command+ " received, from "+npcName+" with parameter"+parameter)
Actor player=Game.GetPlayer() as Actor
if (command=="ExtCmdDance")
; Actor herikaActor = Game.getFormEx(AIAgentFunctions.getHerikaFormId()) as Actor
;to do, find target actor in parameter
AIAgentFunctions.requestMessage("command@"+command+"@"+parameter+"@"+npcName+" dances with "+parameter,"funcret"); // Pass return function to LLM
endif
EndEvent
// Name of the function. This is what will be offered to LLM. Can be overwrited by LANG.
$GLOBALS["F_NAMES"]["ExtCmdHeal"]="Heal";
// Description. This is what will be offered to LLM. Can be overwrited by LANG.
$GLOBALS["F_TRANSLATIONS"]["ExtCmdHeal"]="Heals target using magic spell";
// $FUNCTION_PARM_INSPECT will contain an enum of visible NPC
// $FUNCTION_PARM_MOVETO will contain an enum of visible places to move
// Function definition (OpenaAI style)
$GLOBALS["FUNCTIONS"][] =
[
"name" => $GLOBALS["F_NAMES"]["ExtCmdHeal"],
"description" => $GLOBALS["F_TRANSLATIONS"]["ExtCmdHeal"],
"parameters" => [
"type" => "object",
"properties" => [
"target" => [
"type" => "string",
"description" => "Target NPC, Actor, or being",
"enum" => $GLOBALS["FUNCTION_PARM_INSPECT"]
]
],
"required" => ["target"],
],
]
;
// Add this function to enabled array
$GLOBALS["ENABLED_FUNCTIONS"][]="ExtCmdHeal";
// From here, is stuff that will be needed once the papyrus plugin make a request of type funcret.
// Stuff to manage the return value of the call function.
// Custom prompt. This will overwrite default cue. This is what we are requesting the LLM to do.
// TEMPLATE_DIALOG is degined in global prompts.php.
$GLOBALS["PROMPTS"]["afterfunc"]["cue"]["ExtCmdHeal"]="{$GLOBALS["HERIKA_NAME"]} comments about {$GLOBALS["PLAYER_NAME"]}'s wounds. {$GLOBALS["TEMPLATE_DIALOG"]}";
// If function is a server function (we need to calculate the result value in web server using php code)
// add a callable to FUNCSERV array
// We should execute our code here.
// This example does not require to do anything.
$GLOBALS["FUNCSERV"]["ExtCmdHeal"]=function() {
global $gameRequest,$returnFunction,$db,$request;
// Probably we want to execute something, and put return value in $returnFunction[3] and $gameRequest[3];
// We could overwrite also $request.
};
// When preparing function return data to LLM, maybe we will need to alter request. return array should only contain argName,request,useFunctionsAgain
// argName is mandatory, is the name of the parameter this function uses
// request is optional, if we need to rewrite request to LLM
// useFunctionsAgain is optional, if we need to expose functions again to LLM
$GLOBALS["FUNCRET"]["ExtCmdHeal"]=function($gameRequest) {
// Example, if papyrus execution gives some error, we will need to rewrite request her.
// BY default, request will be $GLOBALS["PROMPTS"]["afterfunc"]["cue"]["ExtCmdHeal"]
// $gameRequest = [type of message,localts,gamets,data]
$GLOBALS["FORCE_MAX_TOKENS"]=48; // We can overwrite anything here using $GLOBALS;
if (stripos($gameRequest[3],"error")!==false) // Papyrus returned error
return ["argName"=>"target","request"=>"{$GLOBALS["HERIKA_NAME"]} says sorry about unable to heal. {$GLOBALS["TEMPLATE_DIALOG"]}"];
else
return ["argName"=>"target"];
};
?>
***/
Scriptname AIAgentFunctions
;Main Functions
int function sendMessage(String a_msg,String a_type) Global Native ; Send message as user input and expects an IA response
int function commandEnded(String command) Global Native
int function commandEndedForActor(String command,string npc) Global Native
int function recordSoundEx(int bindedKey) Global Native
int function stopRecording(int bindedKey) Global Native
int function setNewActionMode(int mode) Global Native
int function logMessage(String a_msg,String type) Global Native ; Send message for logging purposes. Doesn't expect response
int function logMessageForActor(String a_msg,String type,String npc) Global Native ; Send message for logging purposes. Doesn't expect response
int function requestMessage(String a_msg,String type) Global Native ; Send message (no user input). expects an IA response
int function requestMessageForActor(String a_msg,String type,String npc) Global Native ; Send message (no user input). expects an IA response
int function setAnimationBusy(int busy,String npc) Global Native
int function sendRequest() Global Native
int function hardResetExpression() Global Native
int function shotAndUpload(String hints,int mode) Global Native
int function isGameVR() Global Native ; 1 if VR
; Conf opts send
int function setConf(String code,float float_value,int int_value,String string_value) Global Native
;Internal
int function get_conf_i(String code) Global Native
int function setAIKeyWord(Actor targetActor) Global Native
; Agent functions
int function setDrivenByAI() Global Native
int function setDrivenByAIA(Actor forcedActor,bool salutation) Global Native
int function removeAgentByName(String name) Global Native
Actor function getClosestAgent() Global Native
Actor function getAgentByName(String npcName) Global Native
Actor[] function findAllNearbyAgents() Global Native
; Helpers
ObjectReference function getLocationMarkerFor(Location loc) Global Native
ObjectReference function getNearestDoor() global Native
ObjectReference function findLocationsToSafeSpawn(float minDistance,bool restriction=true) global Native
int Function isUsingFurniture(Actor actor) global Native
; Test functions
int function sendAllVoices() Global Native
int function testAddAllNPCAround() Global Native
int function testRemoveAll() Global Native
; Legacy
int function getHerikaFormId() Global Native
The CHIM Twitch bot plugin is an agent of chaos an integration that allows Twitch chat to interact and control AI NPCs in Skyrim.
Viewers will be able to prompt the director to make scenes happen ingame. Also they can say stuff on behalf of the player.
With such power we have ensured to add security controls to tailor the bot to work for your stream setup.
Without getting too philosophical we think this simple plugin completely changes how you, and your viewers, will interact with games.
Here are some examples of use cases for our Twitch Bot:
The repo for our Twitch Bot can be found here: https://github.com/RANGROO/CHIM-Twitch-Bot