diff --git a/README.md b/README.md index 7eb7b59..8f6e5e6 100644 --- a/README.md +++ b/README.md @@ -30,8 +30,9 @@ PS: This is my first public library, then I'm sorry for the lack of knowledge of - [x] Develop a native objects for csharp - [x] Migrate last fixes and changes from rivescript-java since jan-2016 (some push a lot) - [x] Create a RSBot Demo Script (By [Efface Studios](https://github.com/effacestudios/)) +- [x] Create documentation with C4 architecture diagrams and DocFX live documentation - [ ] Complete test based on rsts -- [ ] Complete the code documentation and adjust appveyor to build and release on tag +- [ ] Adjust appveyor to build and release on tag - [ ] [...] @@ -44,7 +45,24 @@ PS: This is my first public library, then I'm sorry for the lack of knowledge of ## DOCUMENTATION -:TODO - C# API documentation +Full documentation lives in the [`docs/`](./docs/) folder and is served as a [DocFX](https://dotnet.github.io/docfx/) site. + +| Section | Description | +|---|---| +| [Getting Started](./docs/getting-started.md) | Install and create your first bot | +| [API Reference](./docs/api/index.md) | Full public API reference | +| [Architecture — C4 Model](./docs/architecture/index.md) | System Context, Container, Component, and Code diagrams | + +### Generating the Live Documentation Site + +Install [DocFX](https://dotnet.github.io/docfx/) and run: + +```bash +dotnet tool install -g docfx +docfx docfx.json --serve +``` + +Then open http://localhost:8080 in your browser to browse the documentation. Also check out the [**RiveScript Community Wiki**](https://github.com/aichaos/rivescript/wiki) for common design patterns and tips & tricks for RiveScript. diff --git a/docfx.json b/docfx.json new file mode 100644 index 0000000..25aadb6 --- /dev/null +++ b/docfx.json @@ -0,0 +1,48 @@ +{ + "metadata": [ + { + "src": [ + { + "files": [ "RiveScript/**.csproj" ], + "src": ".." + } + ], + "dest": "docs/api", + "disableGitFeatures": false, + "disableDefaultFilter": false + } + ], + "build": { + "content": [ + { + "files": [ "**/*.{md,yml}" ], + "src": "docs", + "dest": "." + }, + { + "files": [ "docs/api/**.yml", "docs/api/index.md" ] + } + ], + "resource": [ + { + "files": [ "docs/images/**" ] + } + ], + "output": "_site", + "globalMetadataFiles": [], + "fileMetadataFiles": [], + "template": [ "default", "modern" ], + "globalMetadata": { + "_appTitle": "RiveScript C# — Documentation", + "_appName": "RiveScript C#", + "_appFooter": "RiveScript C# — MIT License | GitHub", + "_enableSearch": true, + "_appLogoPath": "", + "_appFaviconPath": "", + "description": "C# implementation of the RiveScript chatbot scripting language interpreter" + }, + "sitemap": { + "baseUrl": "https://fabioravila.github.io/rivescript-csharp" + } + } +} diff --git a/docs/api/index.md b/docs/api/index.md new file mode 100644 index 0000000..5421a0d --- /dev/null +++ b/docs/api/index.md @@ -0,0 +1,67 @@ +# API Reference + +This section documents the complete public API of the **RiveScript C#** library. + +--- + +## Primary Entry Point + +| Class | Description | +|-------|-------------| +| [`RiveScriptEngine`](RiveScriptEngine.md) | Main interpreter class — load scripts, configure the engine, and get replies | +| [`Config`](Config.md) | Configuration options passed to the `RiveScriptEngine` constructor | +| [`ErrorMessages`](ErrorMessages.md) | Customise the error reply strings returned by the engine | + +--- + +## Session Management + +| Type | Description | +|------|-------------| +| [`ISessionManager`](ISessionManager.md) | Interface for user-session storage backends | +| [`ConcurrentDictionarySessionManager`](ConcurrentDictionarySessionManager.md) | Default thread-safe in-memory implementation | +| [`NoOpSessionManager`](NoOpSessionManager.md) | No-op implementation (stateless bots) | +| [`UserData`](UserData.md) | Per-user variable bag, history, and topic pointer | +| [`History`](History.md) | Rolling window of the last N input/reply pairs | +| [`ThawAction`](ThawAction.md) | Enumeration controlling what happens to a thawed session snapshot | + +--- + +## Object Handlers & Macros + +| Type | Description | +|------|-------------| +| [`IObjectHandler`](IObjectHandler.md) | Interface for language handlers that execute `> object` blocks | +| [`ISubroutine`](ISubroutine.md) | Minimal single-method interface for a named subroutine | +| [`DelegateMacro`](DelegateMacro.md) | Wraps a `Func<>` as an `ISubroutine` | + +--- + +## AST (Abstract Syntax Tree) + +| Class | Description | +|-------|-------------| +| [`Root`](Root.md) | Root of the parsed AST | +| [`Begin`](Begin.md) | Holds global `!define` data | +| [`Topic`](Topic.md) | Groups triggers by topic name | +| [`Trigger`](Trigger.md) | Single `+` pattern with its replies and conditions | +| [`ObjectMacro`](ObjectMacro.md) | Represents one `> object` block | + +--- + +## Logging + +| Type | Description | +|------|-------------| +| [`ILogger`](ILogger.md) | Four-level logging interface | +| [`ConsoleLogger`](ConsoleLogger.md) | Console-based logger | +| [`EmptyLogger`](EmptyLogger.md) | Silent (no-op) logger (default) | + +--- + +## Enumerations & Constants + +| Type | Description | +|------|-------------| +| [`ConcatMode`](ConcatMode.md) | Line continuation mode: `NONE`, `NEWLINE`, `SPACE` | +| [`Constants`](Constants.md) | Library-wide string constants | diff --git a/docs/architecture/c4-code.md b/docs/architecture/c4-code.md new file mode 100644 index 0000000..0e47602 --- /dev/null +++ b/docs/architecture/c4-code.md @@ -0,0 +1,319 @@ +# C4 Level 4 — Code + +The Code (class) diagram shows the key classes, their public members, and relationships inside the **RiveScript C# library**. + +> Only the most important public members are shown for clarity. + +--- + +## Core Classes + +```mermaid +classDiagram + class RiveScriptEngine { + +bool IsErrReply(string reply) + +string getVersion() + +bool loadFile(string file) + +bool loadDirectory(string path) + +bool loadDirectory(string path, string[] exts) + +bool stream(string code) + +bool stream(string[] code) + +void sortReplies() + +string reply(string username, string message) + +bool setGlobal(string name, string value) + +string getGlobal(string name) + +bool setVariable(string name, string value) + +string getVariable(string name) + +bool setUservar(string user, string name, string value) + +string getUserVar(string user, string name) + +bool setSubroutine(string name, ISubroutine sub) + +void setHandler(string name, IObjectHandler handler) + +ISessionManager getSessionManager() + +string currentUser() + +ErrorMessages errors + } + + class Config { + +bool debug + +bool utf8 + +bool strict + +bool throwExceptions + +bool forceCase + +int depth + +ConcatMode concat + +ILogger logger + +ISessionManager sessionManager + +ErrorMessages errors + +Action~string~ onDebug + +string unicodePonctuations + +$Config Default + +$Config UTF8 + +$Config Debug + +$Config DebugUTF8 + } + + class ErrorMessages { + +string deepRecursion + +string replyNotMatched + +string replyNotFound + +string objectNotFound + +$ErrorMessages Default + +ErrorMessages AdjustDefaults() + } + + RiveScriptEngine --> Config : configured by + RiveScriptEngine --> ErrorMessages : uses +``` + +--- + +## Parser & AST + +```mermaid +classDiagram + class Parser { + +Parser(TopicManager topicManager) + +Parser(ParserConfig config, TopicManager topicManager) + +Root parse(string filename, string[] code) + } + + class ParserConfig { + +bool strict + +bool utf8 + +bool forceCase + +ConcatMode concat + +ILogger logger + +$ParserConfig Default + } + + class Root { + +Begin begin + +List~ObjectMacro~ objects + +IDictionary~string,Topic~ topics + +void addTopic(Topic topic) + +void addObject(ObjectMacro obj) + } + + class Begin { + +IDictionary~string,string~ globals + +IDictionary~string,string~ vars + +IDictionary~string,string~ subs + +IDictionary~string,string~ person + +IDictionary~string,ICollection~string~~ arrays + } + + class Topic { + +string name + +bool hasPrevious + +ICollection~string~ includes + +ICollection~string~ inherits + +Trigger getTrigger(string pattern) + +void addIncludes(string topic) + +void addInherits(string topic) + +void sortTriggers() + +string[] getTopics() + } + + class Trigger { + +string pattern + +ICollection~string~ reply + +ICollection~string~ condition + +string redirect + +bool hasOwnCode + +IDictionary~string,ICollection~string~~ previous + } + + class ObjectMacro { + +string Name + +string Language + +List~string~ Code + } + + Parser --> ParserConfig : uses + Parser ..> Root : produces + Root *-- Begin : contains + Root *-- "0..*" Topic : contains + Root *-- "0..*" ObjectMacro : contains + Topic *-- "0..*" Trigger : contains +``` + +--- + +## Topic Manager & Sorting + +```mermaid +classDiagram + class TopicManager { + +Topic getTopic(string name) + +bool exists(string name) + +string[] listTopicsName() + +IDictionary~string,Topic~ listTopics() + +void sortReplies() + } + + class SortBuffer { + +string[] topics + +IDictionary~string,string[]~ thats + } + + class SortTrack { + +int wildcards + +int optionals + +int namedWildcards + +int wordCount + } + + class SortedTriggerEntry { + +string trigger + +Trigger pointer + } + + TopicManager --> "0..*" Topic : manages + TopicManager --> SortBuffer : builds + SortBuffer --> "0..*" SortedTriggerEntry : contains + SortTrack ..> SortedTriggerEntry : helps rank +``` + +--- + +## Session Management + +```mermaid +classDiagram + class ISessionManager { + <> + +UserData init(string username) + +void set(string username, string name, string value) + +void set(string username, IDictionary~string,string~ vars) + +void addHistory(string username, string input, string reply) + +void setLastMatch(string username, string trigger) + +string get(string username, string name) + +UserData get(string username) + +IDictionary~string,UserData~ getAll() + +string getLastMatch(string username) + +History getHistory(string username) + +void clear(string username) + +void clearAll() + +void freeze(string username) + +void thaw(string username, ThawAction action) + +void remove(string username, string name) + } + + class ConcurrentDictionarySessionManager { + +UserData init(string username) + +void set(...) + +UserData get(string username) + +void freeze(string username) + +void thaw(string username, ThawAction action) + } + + class NoOpSessionManager { + +UserData init(string username) + +void set(...) + } + + class UserData { + +string username + +string lastMatch + +History history + +void setVariable(string name, string value) + +string getVariable(string name) + +bool removeVariable(string name) + +IDictionary~string,string~ getVariables() + +History getHistory() + } + + class History { + +IList~string~ input + +IList~string~ reply + +void add(string input, string reply) + } + + class ThawAction { + <> + KEEP + THAW + DISCARD + } + + ISessionManager <|.. ConcurrentDictionarySessionManager : implements + ISessionManager <|.. NoOpSessionManager : implements + ISessionManager ..> UserData : returns + UserData *-- History : contains + ISessionManager ..> ThawAction : uses +``` + +--- + +## Object Handlers & Macros + +```mermaid +classDiagram + class IObjectHandler { + <> + +void Load(string name, string[] code) + +string Call(string name, RiveScriptEngine rs, string[] args) + } + + class CSharpObjectHandler { + +void Load(string name, string[] code) + +string Call(string name, RiveScriptEngine rs, string[] args) + +void AddSubroutine(string name, ISubroutine sub) + } + + class ISubroutine { + <> + +string Call(RiveScriptEngine rs, string[] args) + } + + class DelegateMacro { + +DelegateMacro(Func~RiveScriptEngine,string[],string~ func) + +string Call(RiveScriptEngine rs, string[] args) + } + + IObjectHandler <|.. CSharpObjectHandler : implements + CSharpObjectHandler --> "0..*" ISubroutine : hosts named + ISubroutine <|.. DelegateMacro : implements +``` + +--- + +## Logging + +```mermaid +classDiagram + class ILogger { + <> + +bool IsDebugEnable + +bool IsWarnEnable + +bool IsTraceEnable + +bool IsErrorEnable + +void Debug(string text) + +void Warn(string text) + +void Warn(string text, string filename, int lineno) + +void Error(string text) + +void Error(Exception exception) + +void Trace(string text) + } + + class ConsoleLogger { + +void Debug(string text) + +void Warn(string text) + +void Error(string text) + +void Trace(string text) + } + + class EmptyLogger { + +void Debug(string text) + +void Warn(string text) + +void Error(string text) + +void Trace(string text) + } + + ILogger <|.. ConsoleLogger : implements + ILogger <|.. EmptyLogger : implements +``` + +--- + +[← Component Diagram](c4-component.md) | [Architecture Overview ↑](index.md) diff --git a/docs/architecture/c4-component.md b/docs/architecture/c4-component.md new file mode 100644 index 0000000..2560f6d --- /dev/null +++ b/docs/architecture/c4-component.md @@ -0,0 +1,137 @@ +# C4 Level 3 — Component + +The Component diagram zooms into the **RiveScriptEngine** and the supporting subsystems to show the key classes and their responsibilities. + +```mermaid +C4Component + title Component Diagram — RiveScript C# Library (RiveScript.dll) + + Container_Boundary(engine_b, "RiveScriptEngine") { + Component(rse, "RiveScriptEngine", "Public API Class", "Exposes loadFile(), stream(), sortReplies(), reply(), setUservar(), etc.") + Component(config, "Config", "POCO", "Holds all startup options: debug, utf8, strict, depth, sessionManager, logger, etc.") + Component(errorMessages, "ErrorMessages", "POCO", "Customisable reply strings for deep-recursion, not-found, etc.") + } + + Container_Boundary(parser_b, "Parser Subsystem") { + Component(parser, "Parser", "Class", "Tokenises RiveScript source line by line and produces an AST Root object") + Component(parserConfig, "ParserConfig", "POCO", "strict, utf8, forceCase, concat settings for the parser") + Component(ast_root, "Root (AST)", "Class", "Root of the parsed AST — holds Begin data and ObjectMacros") + Component(ast_begin, "Begin (AST)", "Class", "Holds !global, !var, !sub, !person, !array definitions") + Component(ast_topic, "Topic (AST)", "Class", "Holds all triggers for one topic") + Component(ast_trigger, "Trigger (AST)", "Class", "One + trigger with its replies, conditions, redirects, and %Previous") + Component(ast_macro, "ObjectMacro (AST)", "Class", "Holds code for an embedded > object ... < object block") + } + + Container_Boundary(topic_b, "Topic Subsystem") { + Component(topicMgr, "TopicManager", "Class", "Registry of all Topic instances; orchestrates inheritance / includes resolution") + Component(sortBuffer, "SortBuffer", "Class", "Per-topic sorted trigger array produced by sortReplies()") + Component(sortTrack, "SortTrack", "Class", "Tracks sort weight for each trigger during sort phase") + } + + Container_Boundary(session_b, "Session Subsystem") { + Component(iSession, "ISessionManager", "Interface", "Contract for user session storage") + Component(concSession, "ConcurrentDictionarySessionManager", "Default Impl", "Thread-safe in-memory session store") + Component(noopSession, "NoOpSessionManager", "Impl", "No-op implementation (useful for stateless bots)") + Component(userData, "UserData", "Class", "Per-user bag of variables, history, frozen snapshot, and current topic") + Component(history, "History", "Class", "Rolling window of last N inputs and replies for a user") + } + + Container_Boundary(macro_b, "Object Handler Subsystem") { + Component(iHandler, "IObjectHandler", "Interface", "Contract for loading and calling embedded code objects") + Component(csharpHandler, "CSharpObjectHandler", "Default Impl", "Compiles and executes C# snippets embedded in .rive scripts") + Component(iSubroutine, "ISubroutine", "Interface", "Single-method contract for a named subroutine callable from scripts") + Component(delegateMacro, "DelegateMacro", "Impl", "Wraps a C# Func<> as an ISubroutine") + } + + Container_Boundary(log_b, "Logging") { + Component(iLogger, "ILogger", "Interface", "Debug/Info/Warn/Error logging contract") + Component(consoleLogger, "ConsoleLogger", "Impl", "Writes to Console.WriteLine") + Component(emptyLogger, "EmptyLogger", "Impl", "Discards all log calls (default)") + } + + Rel(rse, config, "Configured by") + Rel(rse, parser, "Delegates script loading to") + Rel(rse, topicMgr, "Queries trigger matches from") + Rel(rse, iSession, "Reads/writes user data via") + Rel(rse, iHandler, "Calls macro code via") + Rel(rse, iLogger, "Logs via") + Rel(parser, parserConfig, "Uses settings from") + Rel(parser, ast_root, "Produces") + Rel(ast_root, ast_begin, "Contains") + Rel(ast_root, ast_topic, "Contains many") + Rel(ast_root, ast_macro, "Contains many") + Rel(ast_topic, ast_trigger, "Contains many") + Rel(topicMgr, ast_topic, "Manages") + Rel(topicMgr, sortBuffer, "Builds and caches") + Rel(sortBuffer, sortTrack, "Uses during sort") + Rel(iSession, userData, "Returns") + Rel(userData, history, "Contains") + Rel(concSession, iSession, "Implements") + Rel(noopSession, iSession, "Implements") + Rel(csharpHandler, iHandler, "Implements") + Rel(delegateMacro, iSubroutine, "Implements") + Rel(csharpHandler, iSubroutine, "Hosts named") + Rel(consoleLogger, iLogger, "Implements") + Rel(emptyLogger, iLogger, "Implements") +``` + +## Component Descriptions + +### RiveScriptEngine Boundary + +| Component | Description | +|---|---| +| `RiveScriptEngine` | The sole public entry-point. Coordinates all subsystems. | +| `Config` | Immutable (or default) options passed at construction time. | +| `ErrorMessages` | Override the reply strings returned on known error conditions. | + +### Parser Subsystem + +| Component | Description | +|---|---| +| `Parser` | Reads source lines, recognises command symbols (`+`, `-`, `^`, `@`, `*`, `>`, `<`, `!`, `%`), and builds the AST. | +| `ParserConfig` | Carries parser-specific settings derived from the top-level `Config`. | +| `Root` | Tree root; holds the global `Begin` node, all `Topic` nodes, and `ObjectMacro` nodes. | +| `Begin` | Stores `! global`, `! var`, `! sub`, `! person`, and `! array` definitions. | +| `Topic (AST)` | An AST node that groups triggers by topic name. | +| `Trigger` | An AST node for one `+` pattern together with its replies, conditions, and redirects. | +| `ObjectMacro` | An AST node for one `> object` block, carrying its language name and code lines. | + +### Topic Subsystem + +| Component | Description | +|---|---| +| `TopicManager` | Central registry. Resolves `includes` and `inherits` links between topics and drives sorting. | +| `SortBuffer` | Holds the sorted trigger array for one topic after `sortReplies()` is called. | +| `SortTrack` | Accumulates weighting metrics (word count, wildcards) for each trigger during the sort pass. | + +### Session Subsystem + +| Component | Description | +|---|---| +| `ISessionManager` | Plug-in interface for user-data storage. | +| `ConcurrentDictionarySessionManager` | Default, thread-safe, in-memory implementation. | +| `NoOpSessionManager` | Ignores all writes; useful for stateless or read-only scenarios. | +| `UserData` | Holds a user's variables map, current topic, history, and frozen snapshot. | +| `History` | Rolling buffer of the last N (default 9) input/reply pairs for a user. | + +### Object Handler Subsystem + +| Component | Description | +|---|---| +| `IObjectHandler` | Interface that all language handlers must implement (`Load`, `Call`). | +| `CSharpObjectHandler` | Default handler; compiles and caches C# macro code with Roslyn/CodeDOM. | +| `ISubroutine` | Minimal interface for a single named subroutine. | +| `DelegateMacro` | Bridges a `Func` to `ISubroutine`. | + +### Logging + +| Component | Description | +|---|---| +| `ILogger` | Four-level (`Debug`, `Info`, `Warn`, `Error`) logging interface. | +| `ConsoleLogger` | Writes all log events to `Console.WriteLine`. | +| `EmptyLogger` | Default; silently discards all log events. | + +--- + +[← Container Diagram](c4-container.md) | [Code Diagram →](c4-code.md) diff --git a/docs/architecture/c4-container.md b/docs/architecture/c4-container.md new file mode 100644 index 0000000..9325d7f --- /dev/null +++ b/docs/architecture/c4-container.md @@ -0,0 +1,52 @@ +# C4 Level 2 — Container + +The Container diagram zooms into the **RiveScript C# Library** and shows the high-level deployable / runnable units. + +> For a library project a "container" is the assembly itself plus the external assets it works with. + +```mermaid +C4Container + title Container Diagram — RiveScript C# Library + + Person(developer, "Application Developer") + + System_Boundary(lib, "RiveScript C# Library (RiveScript.dll)") { + Container(engine, "RiveScriptEngine", "C# Class", "Primary entry-point. Loads scripts, exposes reply() API, manages bot state") + Container(parser, "Parser", "C# Class", "Lexes and parses RiveScript source into an AST") + Container(topicMgr, "TopicManager", "C# Class", "Stores and indexes topics, triggers, and sort buffers") + Container(sessionMgr, "SessionManager", "C# Interface + Impl", "Manages per-user variables, history, and topic state") + Container(objectHandler, "ObjectHandler", "C# Interface + Impl", "Executes embedded code macros inside scripts") + Container(sorting, "Sorting Engine", "C# Classes", "Sorts triggers by specificity for accurate matching") + } + + System_Ext(scripts, "RiveScript Scripts (.rive/.rs)", "Plain-text conversation rules") + System_Ext(externalStore, "External Session Store", "Optional: database, Redis, etc.") + + Rel(developer, engine, "Creates and configures", "new RiveScriptEngine(config)") + Rel(developer, engine, "Calls", "loadFile() / stream() / reply()") + Rel(engine, parser, "Delegates script loading to") + Rel(parser, topicMgr, "Populates") + Rel(engine, topicMgr, "Queries for matching triggers") + Rel(engine, sessionMgr, "Reads/writes user session data") + Rel(engine, objectHandler, "Invokes embedded code macros via") + Rel(engine, sorting, "Calls sortReplies() on") + Rel(parser, scripts, "Reads") + Rel(sessionMgr, externalStore, "Optionally persists to", "custom ISessionManager impl") + + UpdateLayoutConfig($c4ShapeInRow="3", $c4BoundaryInRow="1") +``` + +## Container Descriptions + +| Container | Technology | Responsibility | +|---|---|---| +| **RiveScriptEngine** | C# class | Main public API. Orchestrates loading, sorting, and reply generation | +| **Parser** | C# class | Tokenises `.rive` source and builds the AST (`Root`, `Begin`, `Topic`, `Trigger`) | +| **TopicManager** | C# class | In-memory store of all topics and their triggers; produces sorted lookup tables | +| **SessionManager** | C# interface (`ISessionManager`) + default impl | Stores per-user variables, conversation history, and current topic | +| **ObjectHandler** | C# interface (`IObjectHandler`) + default C# impl | Executes code defined in `> object … < object` blocks | +| **Sorting Engine** | C# utility classes | Ranks triggers by word-count and specificity so the best match wins | + +--- + +[← System Context](c4-context.md) | [Component Diagram →](c4-component.md) diff --git a/docs/architecture/c4-context.md b/docs/architecture/c4-context.md new file mode 100644 index 0000000..904f982 --- /dev/null +++ b/docs/architecture/c4-context.md @@ -0,0 +1,38 @@ +# C4 Level 1 — System Context + +The System Context diagram shows the **RiveScript C# library** in relation to its users and the external artefacts it depends on. + +```mermaid +C4Context + title System Context — RiveScript C# Interpreter + + Person(developer, "Application Developer", "Builds a .NET application that needs a chatbot") + + System(rivescript, "RiveScript C# Library", "Interprets RiveScript scripts and returns replies for user messages") + + System_Ext(scripts, "RiveScript Scripts (.rive/.rs)", "Plain-text script files that define the bot's conversation rules") + System_Ext(hostApp, "Host Application", "Any .NET app: web API, desktop, console, service, etc.") + System_Ext(sessionStore, "Session Storage", "Optional external storage for user session data (e.g., database, Redis)") + + Rel(developer, hostApp, "Builds and runs") + Rel(hostApp, rivescript, "Uses", "calls loadFile/stream, reply()") + Rel(developer, scripts, "Authors", "writes .rive files") + Rel(rivescript, scripts, "Reads and parses") + Rel(rivescript, sessionStore, "Reads / writes user vars", "via ISessionManager (optional)") + + UpdateLayoutConfig($c4ShapeInRow="3", $c4BoundaryInRow="1") +``` + +## Description + +| Actor / System | Role | +|---|---| +| **Application Developer** | Authors `.rive` script files and integrates the library into a .NET application | +| **RiveScript C# Library** | The core system — parses scripts, matches user input to triggers, and returns replies | +| **RiveScript Scripts** | Plain-text `.rive` / `.rs` files that define conversation rules | +| **Host Application** | Any .NET application (web API, console, desktop, background service) that embeds the library | +| **Session Storage** | Optional external store for user variables/history; defaults to an in-memory `ConcurrentDictionary` | + +--- + +[← Architecture Overview](index.md) | [Container Diagram →](c4-container.md) diff --git a/docs/architecture/index.md b/docs/architecture/index.md new file mode 100644 index 0000000..da25a2f --- /dev/null +++ b/docs/architecture/index.md @@ -0,0 +1,31 @@ +# Architecture — C4 Model + +This section describes the architecture of the **RiveScript C#** library using the [C4 model](https://c4model.com/). +The C4 model provides four levels of abstraction: **Context**, **Container**, **Component**, and **Code**. + +--- + +## Contents + +| Level | Description | +|-------|-------------| +| [Level 1 — System Context](c4-context.md) | Who uses the library and what external things it interacts with | +| [Level 2 — Container](c4-container.md) | High-level building blocks of the system | +| [Level 3 — Component](c4-component.md) | Key components inside the library | +| [Level 4 — Code](c4-code.md) | Class-level design of the core components | + +--- + +## Architecture Overview + +RiveScript C# is a **single-assembly .NET library** (`RiveScript.dll`). It: + +- Reads `.rive` / `.rs` script files (or streamed strings) +- Parses them into an in-memory Abstract Syntax Tree (AST) +- Sorts and indexes the triggers for efficient matching +- Responds to user messages by matching triggers and evaluating replies +- Manages per-user session data through a pluggable `ISessionManager` +- Allows embedding of C# code inside scripts via `IObjectHandler` / `ISubroutine` + +The library is designed to be embedded directly into any .NET application — +desktop, web, mobile, or service — without any external dependencies. diff --git a/docs/getting-started.md b/docs/getting-started.md new file mode 100644 index 0000000..fda21e4 --- /dev/null +++ b/docs/getting-started.md @@ -0,0 +1,224 @@ +# Getting Started + +This guide walks you through installing the library and building your first chatbot. + +--- + +## Installation + +### Via NuGet (recommended) + +```bash +dotnet add package RiveScript +``` + +Or in the NuGet Package Manager Console: + +```powershell +Install-Package RiveScript +``` + +### From Source + +```bash +git clone https://github.com/fabioravila/rivescript-csharp.git +cd rivescript-csharp +# Build with Visual Studio or MSBuild +msbuild RiveScript.sln /p:Configuration=Release +``` + +--- + +## Creating Your First Bot + +### 1 — Create a RiveScript Engine + +```csharp +using RiveScript; + +// Default engine (strict mode, no debug, UTF-8 disabled) +var rs = new RiveScriptEngine(); + +// Or with custom options +var rs = new RiveScriptEngine(new Config +{ + debug = false, + utf8 = true, + strict = true, + depth = 50 +}); +``` + +### 2 — Load RiveScript Content + +There are three ways to load script content into the engine: + +**a) Load a single file** + +```csharp +rs.loadFile("brain/greetings.rive"); +``` + +**b) Load an entire directory** + +```csharp +rs.loadDirectory("brain/"); // loads all .rive and .rs files +rs.loadDirectory("brain/", new[] { ".rive" }); // custom extension filter +``` + +**c) Stream a string directly (great for tests)** + +```csharp +rs.stream(@" + + hello + - Hi there! + + + goodbye + - Bye bye! +"); +``` + +### 3 — Sort Replies + +After loading all scripts, call `sortReplies()` **once** before the first `reply()` call. +This builds the internal sort buffers for accurate trigger matching. + +```csharp +rs.sortReplies(); +``` + +### 4 — Get a Reply + +```csharp +string answer = rs.reply("user123", "hello"); +Console.WriteLine(answer); // Hi there! +``` + +--- + +## RiveScript Syntax Cheat-Sheet + +| Symbol | Meaning | Example | +|--------|---------|---------| +| `!` | Define | `! var name = Aiden` | +| `+` | Trigger | `+ hello bot` | +| `-` | Reply | `- Hello, human!` | +| `^` | Continue (multi-line reply) | `^ More text here` | +| `@` | Redirect | `@ greetings` | +| `%` | Previous | `% what is your name` | +| `*` | Condition | `* > 18 => You are an adult.` | +| `>` | Label (topic/object) | `> topic mytopic` | +| `<` | End label | `< topic` | + +--- + +## Configuration Reference + +| Property | Type | Default | Description | +|----------|------|---------|-------------| +| `debug` | `bool` | `false` | Enable debug logging | +| `strict` | `bool` | `true` | Strict parse mode — throw on invalid syntax | +| `utf8` | `bool` | `false` | Enable UTF-8 mode (allows Unicode in triggers) | +| `depth` | `int` | `50` | Maximum recursion depth for reply redirections | +| `forceCase` | `bool` | `false` | Force lowercase on all triggers and user input | +| `concat` | `ConcatMode` | `NONE` | Line continuation mode (`NONE`, `NEWLINE`, `SPACE`) | +| `throwExceptions` | `bool` | `false` | Throw `RiveScriptException` instead of returning error strings | +| `logger` | `ILogger` | `EmptyLogger` | Inject a custom logger | +| `sessionManager` | `ISessionManager` | `ConcurrentDictionarySessionManager` | Custom session storage | +| `errors` | `ErrorMessages` | `ErrorMessages.Default` | Customise error reply strings | + +--- + +## Topics + +Topics let you group triggers and create context-aware conversations: + +```rivescript +> topic greetings + + + hello + - Hi! How can I help you today? + + + bye + - Goodbye! {topic=random} + +< topic +``` + +Switch a user to a topic programmatically: + +```csharp +rs.setUservar("user123", "topic", "greetings"); +``` + +--- + +## Object Macros (Embedded C#) + +You can embed C# code directly in `.rive` scripts: + +```rivescript +> object add csharp + int a = int.Parse(args[0]); + int b = int.Parse(args[1]); + return (a + b).ToString(); +< object + ++ add [*] and [*] +- The sum is: add +``` + +Or register a subroutine from C#: + +```csharp +rs.setSubroutine("greet", new DelegateMacro((engine, args) => +{ + return $"Hello, {args[0]}!"; +})); +``` + +--- + +## Custom Session Manager + +Implement `ISessionManager` to store user data in a database, Redis, or any other backend: + +```csharp +public class MySessionManager : ISessionManager +{ + // Implement all interface members ... +} + +var rs = new RiveScriptEngine(new Config +{ + sessionManager = new MySessionManager() +}); +``` + +--- + +## Error Handling + +By default the engine returns error strings (e.g. `"ERR: Deep Recursion"`) instead of throwing. +You can check for errors with: + +```csharp +if (rs.IsErrReply(reply)) +{ + // handle error +} +``` + +Or enable exception throwing: + +```csharp +var rs = new RiveScriptEngine(new Config { throwExceptions = true }); +``` + +--- + +## Next Steps + +- [API Reference](api/index.md) — full API documentation +- [Architecture](architecture/index.md) — C4 diagrams and design overview +- [RiveScript Community Wiki](https://github.com/aichaos/rivescript/wiki) diff --git a/docs/index.md b/docs/index.md new file mode 100644 index 0000000..16c8800 --- /dev/null +++ b/docs/index.md @@ -0,0 +1,69 @@ +# RiveScript C# — Documentation + +[![Build status](https://ci.appveyor.com/api/projects/status/crwboe7fa6aseqvc/branch/master?svg=true)](https://ci.appveyor.com/project/fabioravila/rivescript-csharp/branch/master) +[![MIT License](https://img.shields.io/github/license/fabioravila/rivescript-csharp.svg)](../LICENSE) + +## What is RiveScript C#? + +**RiveScript C#** is a C# implementation of the [RiveScript](http://www.rivescript.com/) scripting language interpreter. RiveScript is a simple scripting language for authoring chatbots and gives you a simple, yet powerful, way to define conversational flows. + +This library implements the [RiveScript 2.0 Working Draft](http://www.rivescript.com/wd/RiveScript.html) and was initially developed based on the official [rivescript-java](https://github.com/aichaos/rivescript-java) interpreter. + +--- + +## Contents + +| Section | Description | +|---|---| +| [Getting Started](getting-started.md) | Install and create your first bot in minutes | +| [API Reference](api/index.md) | Full public API reference for `RiveScriptEngine` and related types | +| [Architecture](architecture/index.md) | C4 architecture diagrams and design overview | + +--- + +## Quick Example + +```csharp +using RiveScript; + +// Create an engine instance +var rs = new RiveScriptEngine(); + +// Load script from a string +rs.stream(@" + + hello bot + - Hello, human! + + + what is your name + - My name is Aiden. +"); + +// Sort replies (required before first call to reply()) +rs.sortReplies(); + +// Get a reply +string answer = rs.reply("localuser", "hello bot"); +Console.WriteLine(answer); // → Hello, human! +``` + +--- + +## Key Features + +- **RiveScript 2.0** compliant interpreter +- **UTF-8** support for multilingual bots +- **Topic inheritance & includes** for modular scripts +- **Wildcards & alternations** in triggers +- **Conditionals** (`*condition - reply`) +- **Substitutions** (input and person substitutions) +- **Object macros** — embed C# code inside `.rive` scripts +- **Custom session managers** (in-memory by default; implement `ISessionManager` for custom storage) +- **Thread-safe** design using `ThreadLocal` user context +- **Configurable** via the `Config` class + +--- + +## Community & Support + +- [RiveScript Community Wiki](https://github.com/aichaos/rivescript/wiki) +- [GitHub Issues](https://github.com/fabioravila/rivescript-csharp/issues) diff --git a/docs/toc.yml b/docs/toc.yml new file mode 100644 index 0000000..00fabef --- /dev/null +++ b/docs/toc.yml @@ -0,0 +1,19 @@ +- name: Home + href: index.md +- name: Getting Started + href: getting-started.md +- name: API Reference + href: api/index.md +- name: Architecture (C4) + href: architecture/index.md + items: + - name: Overview + href: architecture/index.md + - name: L1 — System Context + href: architecture/c4-context.md + - name: L2 — Container + href: architecture/c4-container.md + - name: L3 — Component + href: architecture/c4-component.md + - name: L4 — Code + href: architecture/c4-code.md