305 lines
9.5 KiB
Text
305 lines
9.5 KiB
Text
# AI Webhook Integration for Chat Interfaces
|
|
# Provides AI-powered webhook endpoints for chat platforms
|
|
|
|
use std
|
|
# Selective imports (ADR-025 Phase 3 Layer 2).
|
|
use platform/ai/lib.nu [ai_process_webhook get_ai_config is_ai_enabled test_ai_connection]
|
|
use platform/settings/loader.nu [get_settings]
|
|
|
|
# Main webhook handler for AI-powered chat integration
|
|
export def ai_webhook_handler [
|
|
payload: record
|
|
--platform: string = "generic"
|
|
--debug
|
|
] {
|
|
if $debug {
|
|
print $"Debug: Received webhook payload: ($payload | to json)"
|
|
}
|
|
|
|
# Validate AI is enabled for webhooks
|
|
let ai_config = (get_ai_config)
|
|
if not $ai_config.enabled or not $ai_config.enable_webhook_ai {
|
|
return {
|
|
success: false
|
|
message: "AI webhook processing is disabled"
|
|
response: "🤖 AI is currently disabled for webhook integrations"
|
|
}
|
|
}
|
|
|
|
# Extract message and metadata based on platform
|
|
let parsed = (parse_webhook_payload $payload $platform)
|
|
|
|
let result = (do {
|
|
let ai_response = (ai_process_webhook $parsed.message $parsed.user_id $parsed.channel)
|
|
|
|
# Format response based on platform
|
|
let formatted_response = (format_webhook_response $ai_response $platform $parsed)
|
|
|
|
{
|
|
success: true
|
|
message: "AI webhook processing successful"
|
|
response: $formatted_response
|
|
user_id: $parsed.user_id
|
|
channel: $parsed.channel
|
|
platform: $platform
|
|
}
|
|
} | complete)
|
|
|
|
if $result.exit_code != 0 {
|
|
{
|
|
success: false
|
|
message: $"AI webhook processing failed: ($result.stderr)"
|
|
response: $"❌ Sorry, I encountered an error: ($result.stderr)"
|
|
user_id: $parsed.user_id
|
|
channel: $parsed.channel
|
|
platform: $platform
|
|
}
|
|
} else {
|
|
$result.stdout | from json
|
|
}
|
|
}
|
|
|
|
# Parse webhook payload based on platform
|
|
def parse_webhook_payload [payload: record, platform: string] {
|
|
match $platform {
|
|
"slack" => {
|
|
{
|
|
message: ($payload.text? // $payload.event?.text? // "")
|
|
user_id: ($payload.user? // $payload.event?.user? // "unknown")
|
|
channel: ($payload.channel? // $payload.event?.channel? // "unknown")
|
|
thread_ts: ($payload.thread_ts? // $payload.event?.thread_ts?)
|
|
bot_id: ($payload.bot_id? // $payload.event?.bot_id?)
|
|
}
|
|
}
|
|
"discord" => {
|
|
{
|
|
message: ($payload.content? // "")
|
|
user_id: ($payload.author?.id? // "unknown")
|
|
channel: ($payload.channel_id? // "unknown")
|
|
guild_id: ($payload.guild_id?)
|
|
message_id: ($payload.id?)
|
|
}
|
|
}
|
|
"teams" => {
|
|
{
|
|
message: ($payload.text? // "")
|
|
user_id: ($payload.from?.id? // "unknown")
|
|
channel: ($payload.conversation?.id? // "unknown")
|
|
conversation_type: ($payload.conversation?.conversationType?)
|
|
}
|
|
}
|
|
"webhook" | "generic" => {
|
|
{
|
|
message: ($payload.message? // $payload.text? // $payload.content? // "")
|
|
user_id: ($payload.user_id? // $payload.user? // "webhook-user")
|
|
channel: ($payload.channel? // $payload.channel_id? // "webhook")
|
|
metadata: $payload
|
|
}
|
|
}
|
|
_ => {
|
|
{
|
|
message: ($payload | to json)
|
|
user_id: "unknown"
|
|
channel: $platform
|
|
raw_payload: $payload
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
# Format AI response for specific platforms
|
|
def format_webhook_response [response: string, platform: string, context: record] {
|
|
match $platform {
|
|
"slack" => {
|
|
let blocks = [
|
|
{
|
|
type: "section"
|
|
text: {
|
|
type: "mrkdwn"
|
|
text: $response
|
|
}
|
|
}
|
|
]
|
|
|
|
if ($context.thread_ts? != null) {
|
|
{
|
|
text: $response
|
|
blocks: $blocks
|
|
thread_ts: $context.thread_ts
|
|
}
|
|
} else {
|
|
{
|
|
text: $response
|
|
blocks: $blocks
|
|
}
|
|
}
|
|
}
|
|
"discord" => {
|
|
{
|
|
content: $response
|
|
embeds: [
|
|
{
|
|
title: "🤖 AI Infrastructure Assistant"
|
|
description: $response
|
|
color: 3447003
|
|
footer: {
|
|
text: "Powered by Provisioning AI"
|
|
}
|
|
}
|
|
]
|
|
}
|
|
}
|
|
"teams" => {
|
|
{
|
|
type: "message"
|
|
text: $response
|
|
attachments: [
|
|
{
|
|
contentType: "application/vnd.microsoft.card.adaptive"
|
|
content: {
|
|
type: "AdaptiveCard"
|
|
version: "1.0"
|
|
body: [
|
|
{
|
|
type: "TextBlock"
|
|
text: "🤖 AI Infrastructure Assistant"
|
|
weight: "bolder"
|
|
}
|
|
{
|
|
type: "TextBlock"
|
|
text: $response
|
|
wrap: true
|
|
}
|
|
]
|
|
}
|
|
}
|
|
]
|
|
}
|
|
}
|
|
_ => {
|
|
{
|
|
message: $response
|
|
timestamp: (date now | format date "%Y-%m-%d %H:%M:%S")
|
|
ai_powered: true
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
# Slack-specific webhook handler
|
|
export def slack_webhook [payload: record, --debug] {
|
|
# Handle Slack challenge verification
|
|
if "challenge" in $payload {
|
|
return {
|
|
challenge: $payload.challenge
|
|
}
|
|
}
|
|
|
|
# Skip bot messages to prevent loops
|
|
if ($payload.event?.bot_id? != null) or ($payload.bot_id? != null) {
|
|
return { success: true, message: "Ignored bot message" }
|
|
}
|
|
|
|
ai_webhook_handler $payload --platform "slack" --debug $debug
|
|
}
|
|
|
|
# Discord-specific webhook handler
|
|
export def discord_webhook [payload: record, --debug] {
|
|
# Skip bot messages to prevent loops
|
|
if ($payload.author?.bot? == true) {
|
|
return { success: true, message: "Ignored bot message" }
|
|
}
|
|
|
|
ai_webhook_handler $payload --platform "discord" --debug $debug
|
|
}
|
|
|
|
# Microsoft Teams-specific webhook handler
|
|
export def teams_webhook [payload: record, --debug] {
|
|
# Skip messages from bots
|
|
if ($payload.from?.name? | str contains "bot") {
|
|
return { success: true, message: "Ignored bot message" }
|
|
}
|
|
|
|
ai_webhook_handler $payload --platform "teams" --debug $debug
|
|
}
|
|
|
|
# Generic webhook handler
|
|
export def generic_webhook [payload: record, --debug] {
|
|
ai_webhook_handler $payload --platform "webhook" --debug $debug
|
|
}
|
|
|
|
# Webhook server using nushell http server
|
|
export def start_webhook_server [
|
|
--port: int = 8080
|
|
--host: string = "0.0.0.0"
|
|
--debug
|
|
] {
|
|
if not (is_ai_enabled) {
|
|
error make {msg: "AI is not enabled - cannot start webhook server"}
|
|
}
|
|
|
|
let ai_config = (get_ai_config)
|
|
if not $ai_config.enable_webhook_ai {
|
|
error make {msg: "AI webhook processing is disabled"}
|
|
}
|
|
|
|
print $"🤖 Starting AI webhook server on ($host):($port)"
|
|
print "Available endpoints:"
|
|
print " POST /webhook/slack - Slack integration"
|
|
print " POST /webhook/discord - Discord integration"
|
|
print " POST /webhook/teams - Microsoft Teams integration"
|
|
print " POST /webhook/generic - Generic webhook"
|
|
print " GET /health - Health check"
|
|
print ""
|
|
|
|
# Note: This is a conceptual implementation
|
|
# In practice, you'd use a proper web server
|
|
print "⚠️ This is a conceptual webhook server."
|
|
print "For production use, integrate with a proper HTTP server like:"
|
|
print " - nginx with nushell CGI"
|
|
print " - Custom HTTP server with nushell backend"
|
|
print " - Serverless functions calling nushell scripts"
|
|
}
|
|
|
|
# Health check endpoint
|
|
export def webhook_health_check [] {
|
|
let ai_config = (get_ai_config)
|
|
let ai_test = (test_ai_connection)
|
|
|
|
{
|
|
status: "healthy"
|
|
ai_enabled: $ai_config.enabled
|
|
ai_webhook_enabled: $ai_config.enable_webhook_ai
|
|
ai_provider: $ai_config.provider
|
|
ai_connection: $ai_test.success
|
|
timestamp: (date now | format date "%Y-%m-%d %H:%M:%S")
|
|
version: "provisioning-ai-v1.0"
|
|
}
|
|
}
|
|
|
|
# Process command-line webhook for testing
|
|
export def test_webhook [
|
|
message: string
|
|
--platform: string = "generic"
|
|
--user: string = "test-user"
|
|
--channel: string = "test-channel"
|
|
--debug
|
|
] {
|
|
let payload = {
|
|
message: $message
|
|
user_id: $user
|
|
channel: $channel
|
|
timestamp: (date now | format date "%Y-%m-%d %H:%M:%S")
|
|
test: true
|
|
}
|
|
|
|
let result = (ai_webhook_handler $payload --platform $platform --debug $debug)
|
|
|
|
print $"Platform: ($platform)"
|
|
print $"User: ($user)"
|
|
print $"Channel: ($channel)"
|
|
print $"Message: ($message)"
|
|
print ""
|
|
print "AI Response:"
|
|
print $result.response
|
|
}
|