Cisco Webex
Communication Tool
Cisco Webex
Overview
Cisco Webex is an enterprise-grade comprehensive collaboration platform. It integrates high-quality video conferencing, team collaboration features, and enterprise-level security, providing large-scale meeting capabilities supporting up to 100,000 participants. With Webex SDK v3, powerful bot development features, and rich webhook integration, it enables the construction of customized enterprise solutions.
Details
Cisco Webex evolved from WebEx, founded in 1995 and later acquired by Cisco, developing into the current Webex platform. It is widely adopted by large enterprises, government agencies, and educational institutions worldwide, particularly known for its high-quality video conferencing and security features.
In 2024-2025, major implementations included large-scale migration to Webex SDK v3, AI Transcription API, Webex Assistant API, Real-time Media API, Advanced Spaces API, new Webhook Events, and significant expansion of Contact Center API. Additionally, Zero Trust Security architecture enhancement, Cloud-Native Infrastructure migration, and WebRTC optimization were implemented, greatly improving stability and performance in enterprise environments.
Webex API provides JavaScript SDK, Python SDK, .NET SDK, REST API, and GraphQL API, enabling bot development, custom application development, and system integration. Integration with Adaptive Cards UI and Webex Devices also provides consistent experiences from meeting room systems to mobile devices.
Pros and Cons
Pros
- Enterprise Security: Zero Trust Security, end-to-end encryption
- Large-scale Meeting Support: Up to 100,000 participants, webinar and broadcast functionality
- Rich APIs/SDKs: JavaScript, Python, .NET SDK, REST API, GraphQL
- Hybrid Environment Support: Hybrid deployment of on-premises and cloud
- AI Feature Integration: Real-time transcription, Webex Assistant, noise removal
- Device Integration: Complete integration with Webex Devices and meeting room systems
- Compliance: HIPAA, FedRAMP, SOC2 certifications
- Global Deployment: Multi-region data centers, low latency
Cons
- High Cost: Enterprise pricing structure, complex licensing system
- Complexity: Configuration and management complexity due to rich features
- Learning Curve: Acquisition costs for administrators and developers
- Cisco Dependency: Dependence on Cisco ecosystem
- Not for Small Enterprises: Excessive for startups and small teams
- Customization Limitations: UI/UX customization constraints
Key Links
- Cisco Webex Official Site
- Webex Developer Portal
- Webex JavaScript SDK
- Webex API Reference
- Webex Bot Framework
- Webex Webhooks
- Cisco DevNet
Code Examples
Bot Development with Webex JavaScript SDK v3
// Webex JavaScript SDK v3 Bot implementation
import { Webex } from '@webex/sdk';
class WebexBot {
constructor(accessToken) {
this.webex = Webex.init({
credentials: {
access_token: accessToken
},
config: {
logger: {
level: 'info'
},
meetings: {
enableExperimentalSupport: true
}
}
});
this.setupEventHandlers();
}
setupEventHandlers() {
// New message events
this.webex.messages.on('created', (message) => {
this.handleMessage(message);
});
// Mention notifications
this.webex.messages.on('created', (message) => {
if (message.mentionedPeople && message.mentionedPeople.includes(this.webex.people.me.id)) {
this.handleMention(message);
}
});
// Meeting events
this.webex.meetings.on('meeting:added', (meeting) => {
console.log('🎥 New meeting created:', meeting.id);
});
// Space creation events
this.webex.rooms.on('created', (room) => {
console.log('🏠 New Space created:', room.title);
});
}
async handleMessage(message) {
// Ignore own messages
if (message.personId === this.webex.people.me.id) return;
console.log(`📩 Message received: ${message.text}`);
// Command processing
if (message.text && message.text.startsWith('/')) {
await this.processCommand(message);
}
// Keyword detection
if (message.text && message.text.toLowerCase().includes('help')) {
await this.sendHelpMessage(message.roomId);
}
// Meeting schedule request
if (message.text && message.text.toLowerCase().includes('schedule meeting')) {
await this.scheduleMeeting(message);
}
}
async processCommand(message) {
const [command, ...args] = message.text.slice(1).split(' ');
switch (command.toLowerCase()) {
case 'ping':
await this.sendMessage(message.roomId, '🏓 Pong!');
break;
case 'status':
await this.sendSystemStatus(message.roomId);
break;
case 'weather':
await this.sendWeatherInfo(message.roomId, args[0] || 'Tokyo');
break;
case 'meeting':
await this.handleMeetingCommand(message, args);
break;
case 'space':
await this.handleSpaceCommand(message, args);
break;
case 'people':
await this.listSpaceMembers(message.roomId);
break;
default:
await this.sendMessage(message.roomId, `❓ Unknown command: ${command}. Type /help for help.`);
}
}
async sendMessage(roomId, text, attachments = null) {
try {
const messageData = {
roomId: roomId,
text: text
};
if (attachments) {
messageData.attachments = attachments;
}
const response = await this.webex.messages.create(messageData);
return response;
} catch (error) {
console.error('Message send error:', error);
}
}
async sendAdaptiveCard(roomId, cardData) {
try {
const attachments = [{
contentType: 'application/vnd.microsoft.card.adaptive',
content: cardData
}];
await this.sendMessage(roomId, 'Card-based message:', attachments);
} catch (error) {
console.error('Adaptive Card send error:', error);
}
}
async sendHelpMessage(roomId) {
const helpCard = {
type: 'AdaptiveCard',
version: '1.4',
body: [
{
type: 'TextBlock',
text: '🤖 Webex Bot Commands',
weight: 'Bolder',
size: 'Medium'
},
{
type: 'TextBlock',
text: 'Available commands:'
},
{
type: 'FactSet',
facts: [
{ title: '/ping', value: 'Connection test' },
{ title: '/status', value: 'System status display' },
{ title: '/weather [city]', value: 'Weather information' },
{ title: '/meeting create', value: 'Create meeting' },
{ title: '/space info', value: 'Space information' },
{ title: '/people', value: 'Member list' }
]
}
],
actions: [
{
type: 'Action.Submit',
title: 'Check System Status',
data: {
action: 'status'
}
}
]
};
await this.sendAdaptiveCard(roomId, helpCard);
}
async sendSystemStatus(roomId) {
const os = require('os');
const uptimeHours = Math.floor(os.uptime() / 3600);
const uptimeMinutes = Math.floor((os.uptime() % 3600) / 60);
const memoryUsage = Math.round((os.totalmem() - os.freemem()) / os.totalmem() * 100);
const statusCard = {
type: 'AdaptiveCard',
version: '1.4',
body: [
{
type: 'TextBlock',
text: '📊 System Status',
weight: 'Bolder',
size: 'Medium'
},
{
type: 'FactSet',
facts: [
{ title: 'Uptime', value: `${uptimeHours}h ${uptimeMinutes}m` },
{ title: 'Memory Usage', value: `${memoryUsage}%` },
{ title: 'Platform', value: `${os.platform()} ${os.arch()}` },
{ title: 'Node.js', value: process.version },
{ title: 'Webex SDK', value: 'Connected ✅' }
]
}
]
};
await this.sendAdaptiveCard(roomId, statusCard);
}
async sendWeatherInfo(roomId, city) {
try {
const response = await fetch(`https://api.openweathermap.org/data/2.5/weather?q=${city}&appid=${process.env.WEATHER_API_KEY}&units=metric`);
const weather = await response.json();
if (weather.cod === 200) {
const weatherCard = {
type: 'AdaptiveCard',
version: '1.4',
body: [
{
type: 'TextBlock',
text: `🌤️ Weather in ${weather.name}`,
weight: 'Bolder',
size: 'Medium'
},
{
type: 'FactSet',
facts: [
{ title: 'Weather', value: weather.weather[0].description },
{ title: 'Temperature', value: `${weather.main.temp}°C` },
{ title: 'Humidity', value: `${weather.main.humidity}%` },
{ title: 'Wind Speed', value: `${weather.wind.speed} m/s` }
]
}
]
};
await this.sendAdaptiveCard(roomId, weatherCard);
} else {
await this.sendMessage(roomId, '❌ Failed to fetch weather information');
}
} catch (error) {
console.error('Weather API error:', error);
await this.sendMessage(roomId, '❌ Error occurred while fetching weather information');
}
}
async handleMeetingCommand(message, args) {
const subCommand = args[0]?.toLowerCase();
switch (subCommand) {
case 'create':
await this.createMeeting(message.roomId, args.slice(1));
break;
case 'list':
await this.listMeetings(message.roomId);
break;
case 'join':
await this.joinMeeting(message.roomId, args[1]);
break;
default:
await this.sendMessage(message.roomId, 'Available meeting commands: create, list, join');
}
}
async createMeeting(roomId, args) {
try {
const title = args.join(' ') || 'Bot Created Meeting';
const startTime = new Date(Date.now() + 10 * 60 * 1000); // 10 minutes later
const endTime = new Date(startTime.getTime() + 60 * 60 * 1000); // 1 hour
const meetingData = {
title: title,
start: startTime.toISOString(),
end: endTime.toISOString(),
timezone: 'UTC',
enabledAutoRecordMeeting: false,
allowAnyUserToBeCoHost: true
};
const meeting = await this.webex.meetings.create(meetingData);
const meetingCard = {
type: 'AdaptiveCard',
version: '1.4',
body: [
{
type: 'TextBlock',
text: '🎥 Meeting Created',
weight: 'Bolder',
size: 'Medium'
},
{
type: 'FactSet',
facts: [
{ title: 'Title', value: meeting.title },
{ title: 'Start Time', value: new Date(meeting.start).toLocaleString() },
{ title: 'Meeting ID', value: meeting.meetingNumber },
{ title: 'Web Link', value: meeting.webLink }
]
}
],
actions: [
{
type: 'Action.OpenUrl',
title: 'Join Meeting',
url: meeting.webLink
}
]
};
await this.sendAdaptiveCard(roomId, meetingCard);
} catch (error) {
console.error('Meeting creation error:', error);
await this.sendMessage(roomId, '❌ Failed to create meeting');
}
}
async listMeetings(roomId) {
try {
const meetings = await this.webex.meetings.list({
from: new Date().toISOString(),
to: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000).toISOString() // Next week
});
if (meetings.items && meetings.items.length > 0) {
const meetingList = meetings.items.slice(0, 5).map(meeting => ({
title: meeting.title,
value: `${new Date(meeting.start).toLocaleString()} - ${meeting.meetingNumber}`
}));
const listCard = {
type: 'AdaptiveCard',
version: '1.4',
body: [
{
type: 'TextBlock',
text: '📅 Upcoming Meetings',
weight: 'Bolder',
size: 'Medium'
},
{
type: 'FactSet',
facts: meetingList
}
]
};
await this.sendAdaptiveCard(roomId, listCard);
} else {
await this.sendMessage(roomId, '📅 No upcoming meetings');
}
} catch (error) {
console.error('Meeting list error:', error);
await this.sendMessage(roomId, '❌ Failed to list meetings');
}
}
async handleSpaceCommand(message, args) {
const subCommand = args[0]?.toLowerCase();
switch (subCommand) {
case 'info':
await this.getSpaceInfo(message.roomId);
break;
case 'create':
await this.createSpace(message.roomId, args.slice(1));
break;
default:
await this.sendMessage(message.roomId, 'Available space commands: info, create');
}
}
async getSpaceInfo(roomId) {
try {
const room = await this.webex.rooms.get(roomId);
const spaceCard = {
type: 'AdaptiveCard',
version: '1.4',
body: [
{
type: 'TextBlock',
text: '🏠 Space Information',
weight: 'Bolder',
size: 'Medium'
},
{
type: 'FactSet',
facts: [
{ title: 'Title', value: room.title },
{ title: 'Type', value: room.type === 'group' ? 'Group' : 'Direct' },
{ title: 'Created', value: new Date(room.created).toLocaleDateString() },
{ title: 'Space ID', value: room.id }
]
}
]
};
await this.sendAdaptiveCard(roomId, spaceCard);
} catch (error) {
console.error('Space info error:', error);
await this.sendMessage(roomId, '❌ Failed to get space information');
}
}
async listSpaceMembers(roomId) {
try {
const memberships = await this.webex.memberships.list({ roomId: roomId });
if (memberships.items && memberships.items.length > 0) {
let memberList = [];
for (const membership of memberships.items.slice(0, 10)) {
const person = await this.webex.people.get(membership.personId);
memberList.push({
title: person.displayName,
value: person.emails[0]
});
}
const membersCard = {
type: 'AdaptiveCard',
version: '1.4',
body: [
{
type: 'TextBlock',
text: `👥 Member List (${memberships.items.length})`,
weight: 'Bolder',
size: 'Medium'
},
{
type: 'FactSet',
facts: memberList
}
]
};
await this.sendAdaptiveCard(roomId, membersCard);
}
} catch (error) {
console.error('Member list error:', error);
await this.sendMessage(roomId, '❌ Failed to list members');
}
}
async uploadFile(roomId, filePath, fileName) {
const fs = require('fs');
try {
const fileBuffer = fs.readFileSync(filePath);
const fileData = {
roomId: roomId,
files: [fileBuffer],
filename: fileName
};
const response = await this.webex.messages.create(fileData);
console.log(`📎 File upload complete: ${fileName}`);
return response;
} catch (error) {
console.error('File upload error:', error);
}
}
async start() {
try {
await this.webex.once('ready');
console.log('🚀 Webex Bot started');
// Get own information
this.webex.people.me = await this.webex.people.get('me');
console.log(`Bot info: ${this.webex.people.me.displayName} (${this.webex.people.me.emails[0]})`);
// Register webhooks (for production)
if (process.env.WEBHOOK_URL) {
await this.registerWebhooks();
}
// Setup periodic tasks
this.setupPeriodicTasks();
} catch (error) {
console.error('Bot start error:', error);
}
}
async registerWebhooks() {
try {
// Message webhook
await this.webex.webhooks.create({
name: 'Bot Messages',
targetUrl: process.env.WEBHOOK_URL + '/messages',
resource: 'messages',
event: 'created'
});
// Meeting webhook
await this.webex.webhooks.create({
name: 'Bot Meetings',
targetUrl: process.env.WEBHOOK_URL + '/meetings',
resource: 'meetings',
event: 'created'
});
console.log('✅ Webhook registration complete');
} catch (error) {
console.error('Webhook registration error:', error);
}
}
setupPeriodicTasks() {
// Hourly health check
setInterval(async () => {
console.log('⏰ Running periodic health check...');
}, 3600000);
// Daily report (every day at 9 AM)
const now = new Date();
const msUntil9AM = new Date(now.getFullYear(), now.getMonth(), now.getDate() + 1, 9, 0, 0, 0) - now;
setTimeout(() => {
setInterval(async () => {
console.log('📊 Generating daily report...');
// Send report to admin space (implementation example)
}, 24 * 60 * 60 * 1000);
}, msUntil9AM);
}
async stop() {
console.log('🛑 Stopping Webex Bot');
// Cleanup process
}
}
// Usage example
async function main() {
const accessToken = process.env.WEBEX_ACCESS_TOKEN;
if (!accessToken) {
console.error('❌ WEBEX_ACCESS_TOKEN not set');
process.exit(1);
}
const bot = new WebexBot(accessToken);
// Graceful shutdown
process.on('SIGINT', async () => {
console.log('🔄 Shutting down...');
await bot.stop();
process.exit(0);
});
await bot.start();
}
main().catch(console.error);
Webex Webhook Server Implementation
// Express.js Webhook receiver server
const express = require('express');
const crypto = require('crypto');
const { Webex } = require('@webex/sdk');
const app = express();
app.use(express.json());
class WebexWebhookServer {
constructor(accessToken, webhookSecret) {
this.webex = Webex.init({
credentials: {
access_token: accessToken
}
});
this.webhookSecret = webhookSecret;
}
// Webhook signature verification
verifyWebhookSignature(req, res, next) {
const signature = req.headers['x-spark-signature'];
const body = JSON.stringify(req.body);
if (this.webhookSecret) {
const expectedSignature = crypto
.createHmac('sha1', this.webhookSecret)
.update(body)
.digest('hex');
if (signature !== expectedSignature) {
return res.status(401).send('Invalid signature');
}
}
next();
}
// Message webhook processing
async handleMessageWebhook(req, res) {
const webhookData = req.body;
try {
// Process only new messages
if (webhookData.event === 'created') {
const message = await this.webex.messages.get(webhookData.data.id);
// Ignore bot's own messages
if (message.personId === (await this.webex.people.get('me')).id) {
return res.status(200).send('OK');
}
console.log(`📩 Webhook received: ${message.text}`);
// Execute bot processing
await this.processMessage(message);
}
res.status(200).send('OK');
} catch (error) {
console.error('Webhook processing error:', error);
res.status(500).send('Server error');
}
}
// Meeting webhook processing
async handleMeetingWebhook(req, res) {
const webhookData = req.body;
try {
console.log('🎥 Meeting webhook received:', webhookData);
// Meeting creation notification
if (webhookData.event === 'created') {
const meeting = webhookData.data;
await this.notifyMeetingCreated(meeting);
}
res.status(200).send('OK');
} catch (error) {
console.error('Meeting webhook processing error:', error);
res.status(500).send('Server error');
}
}
async processMessage(message) {
// CI/CD notification processing
if (message.text && message.text.includes('deployment')) {
await this.handleDeploymentNotification(message);
}
// GitHub integration
if (message.text && message.text.includes('github')) {
await this.handleGitHubIntegration(message);
}
// Alert processing
if (message.text && message.text.toLowerCase().includes('alert')) {
await this.handleAlertMessage(message);
}
}
async handleDeploymentNotification(message) {
// Notify deployment status to Webex
const deployCard = {
type: 'AdaptiveCard',
version: '1.4',
body: [
{
type: 'TextBlock',
text: '🚀 Deployment Notification',
weight: 'Bolder',
size: 'Medium'
},
{
type: 'FactSet',
facts: [
{ title: 'Status', value: 'In Progress' },
{ title: 'Environment', value: 'Production' },
{ title: 'Start Time', value: new Date().toLocaleString() }
]
}
],
actions: [
{
type: 'Action.OpenUrl',
title: 'View Logs',
url: 'https://your-ci-cd-platform.com/logs'
}
]
};
await this.sendAdaptiveCard(message.roomId, deployCard);
}
async sendAdaptiveCard(roomId, cardData) {
const attachments = [{
contentType: 'application/vnd.microsoft.card.adaptive',
content: cardData
}];
await this.webex.messages.create({
roomId: roomId,
text: 'Card-based message:',
attachments: attachments
});
}
// System monitoring integration
async sendSystemAlert(roomId, alertData) {
const severity = alertData.severity || 'warning';
const colors = {
'critical': 'attention',
'warning': 'warning',
'info': 'good'
};
const alertCard = {
type: 'AdaptiveCard',
version: '1.4',
body: [
{
type: 'TextBlock',
text: `🚨 System Alert: ${alertData.title}`,
weight: 'Bolder',
size: 'Medium',
color: colors[severity]
},
{
type: 'TextBlock',
text: alertData.description,
wrap: true
},
{
type: 'FactSet',
facts: [
{ title: 'Severity', value: severity.toUpperCase() },
{ title: 'Service', value: alertData.service },
{ title: 'Timestamp', value: new Date(alertData.timestamp).toLocaleString() }
]
}
],
actions: [
{
type: 'Action.OpenUrl',
title: 'View Dashboard',
url: alertData.dashboardUrl || 'https://your-monitoring-dashboard.com'
}
]
};
await this.sendAdaptiveCard(roomId, alertCard);
}
startServer(port = 3000) {
// Webhook endpoint setup
app.post('/webhooks/messages',
this.verifyWebhookSignature.bind(this),
this.handleMessageWebhook.bind(this)
);
app.post('/webhooks/meetings',
this.verifyWebhookSignature.bind(this),
this.handleMeetingWebhook.bind(this)
);
// External system integration endpoints
app.post('/api/alert', async (req, res) => {
const { roomId, alertData } = req.body;
try {
await this.sendSystemAlert(roomId, alertData);
res.status(200).json({ success: true });
} catch (error) {
console.error('Alert send error:', error);
res.status(500).json({ error: 'Failed to send alert' });
}
});
// GitHub Webhook
app.post('/api/github', async (req, res) => {
const githubEvent = req.headers['x-github-event'];
const payload = req.body;
try {
await this.handleGitHubEvent(githubEvent, payload);
res.status(200).send('OK');
} catch (error) {
console.error('GitHub webhook processing error:', error);
res.status(500).send('Error');
}
});
// Health check
app.get('/health', (req, res) => {
res.status(200).json({
status: 'healthy',
timestamp: new Date().toISOString()
});
});
app.listen(port, () => {
console.log(`🚀 Webex Webhook server started on port ${port}`);
});
}
async handleGitHubEvent(eventType, payload) {
const roomId = process.env.GITHUB_NOTIFICATION_ROOM;
if (!roomId) return;
switch (eventType) {
case 'push':
await this.handleGitHubPush(roomId, payload);
break;
case 'pull_request':
await this.handleGitHubPR(roomId, payload);
break;
case 'issues':
await this.handleGitHubIssue(roomId, payload);
break;
}
}
async handleGitHubPush(roomId, payload) {
const pushCard = {
type: 'AdaptiveCard',
version: '1.4',
body: [
{
type: 'TextBlock',
text: `🚀 Push to ${payload.repository.full_name}`,
weight: 'Bolder',
size: 'Medium'
},
{
type: 'FactSet',
facts: [
{ title: 'Commits', value: payload.commits.length.toString() },
{ title: 'Branch', value: payload.ref.split('/').pop() },
{ title: 'Pusher', value: payload.pusher.name }
]
},
{
type: 'TextBlock',
text: '**Latest commit:**',
weight: 'Bolder'
}
]
};
// Add latest commit details
if (payload.commits.length > 0) {
const latestCommit = payload.commits[0];
pushCard.body.push({
type: 'FactSet',
facts: [
{ title: 'Message', value: latestCommit.message },
{ title: 'Commit ID', value: latestCommit.id.substring(0, 7) },
{ title: 'Author', value: latestCommit.author.name }
]
});
}
pushCard.actions = [
{
type: 'Action.OpenUrl',
title: 'View Changes',
url: payload.compare
}
];
await this.sendAdaptiveCard(roomId, pushCard);
}
}
// Usage example
async function startWebhookServer() {
const accessToken = process.env.WEBEX_ACCESS_TOKEN;
const webhookSecret = process.env.WEBEX_WEBHOOK_SECRET;
if (!accessToken) {
console.error('❌ WEBEX_ACCESS_TOKEN not set');
process.exit(1);
}
const webhookServer = new WebexWebhookServer(accessToken, webhookSecret);
webhookServer.startServer(process.env.PORT || 3000);
}
startWebhookServer().catch(console.error);
Python SDK Bot Development
# Python Webex Bot implementation using webexteamssdk
import os
import asyncio
from webexteamssdk import WebexTeamsAPI
from flask import Flask, request, jsonify
import threading
class WebexPythonBot:
def __init__(self, access_token):
self.api = WebexTeamsAPI(access_token=access_token)
self.bot_id = self.api.people.me().id
def handle_message(self, message_data):
"""Message processing"""
message = self.api.messages.get(message_data['id'])
# Ignore bot's own messages
if message.personId == self.bot_id:
return
print(f"📩 Message received: {message.text}")
# Command processing
if message.text and message.text.startswith('/'):
self.process_command(message)
# Keyword detection
elif message.text and 'help' in message.text.lower():
self.send_help_message(message.roomId)
def process_command(self, message):
"""Command processing"""
parts = message.text[1:].split()
command = parts[0].lower()
args = parts[1:] if len(parts) > 1 else []
if command == 'ping':
self.send_message(message.roomId, '🏓 Pong!')
elif command == 'status':
self.send_system_status(message.roomId)
elif command == 'weather':
city = args[0] if args else 'Tokyo'
self.send_weather_info(message.roomId, city)
elif command == 'meeting':
if args and args[0] == 'create':
self.create_meeting(message.roomId, ' '.join(args[1:]))
else:
self.send_message(message.roomId, f"❓ Unknown command: {command}")
def send_message(self, room_id, text, attachments=None):
"""Send message"""
try:
if attachments:
self.api.messages.create(
roomId=room_id,
text=text,
attachments=attachments
)
else:
self.api.messages.create(roomId=room_id, text=text)
except Exception as e:
print(f"Message send error: {e}")
def send_adaptive_card(self, room_id, card_data):
"""Send Adaptive Card"""
attachments = [{
"contentType": "application/vnd.microsoft.card.adaptive",
"content": card_data
}]
self.send_message(room_id, "Card-based message:", attachments)
def send_help_message(self, room_id):
"""Send help message"""
help_card = {
"type": "AdaptiveCard",
"version": "1.4",
"body": [
{
"type": "TextBlock",
"text": "🤖 Webex Python Bot",
"weight": "Bolder",
"size": "Medium"
},
{
"type": "FactSet",
"facts": [
{"title": "/ping", "value": "Connection test"},
{"title": "/status", "value": "System status"},
{"title": "/weather [city]", "value": "Weather information"},
{"title": "/meeting create [title]", "value": "Create meeting"}
]
}
]
}
self.send_adaptive_card(room_id, help_card)
def send_system_status(self, room_id):
"""Send system status"""
import psutil
import platform
cpu_percent = psutil.cpu_percent()
memory = psutil.virtual_memory()
status_card = {
"type": "AdaptiveCard",
"version": "1.4",
"body": [
{
"type": "TextBlock",
"text": "📊 System Status",
"weight": "Bolder",
"size": "Medium"
},
{
"type": "FactSet",
"facts": [
{"title": "CPU Usage", "value": f"{cpu_percent}%"},
{"title": "Memory Usage", "value": f"{memory.percent}%"},
{"title": "Platform", "value": f"{platform.system()} {platform.machine()}"},
{"title": "Python", "value": platform.python_version()}
]
}
]
}
self.send_adaptive_card(room_id, status_card)
def create_meeting(self, room_id, title):
"""Create meeting"""
try:
from datetime import datetime, timedelta
# Create 1-hour meeting starting 10 minutes from now
start_time = datetime.now() + timedelta(minutes=10)
end_time = start_time + timedelta(hours=1)
meeting_data = {
'title': title or 'Python Bot Created Meeting',
'start': start_time.isoformat(),
'end': end_time.isoformat(),
'timezone': 'UTC',
'enabledAutoRecordMeeting': False,
'allowAnyUserToBeCoHost': True
}
meeting = self.api.meetings.create(**meeting_data)
meeting_card = {
"type": "AdaptiveCard",
"version": "1.4",
"body": [
{
"type": "TextBlock",
"text": "🎥 Meeting Created",
"weight": "Bolder",
"size": "Medium"
},
{
"type": "FactSet",
"facts": [
{"title": "Title", "value": meeting.title},
{"title": "Start Time", "value": start_time.strftime("%Y-%m-%d %H:%M")},
{"title": "Meeting Number", "value": meeting.meetingNumber},
{"title": "Join URL", "value": meeting.webLink}
]
}
],
"actions": [
{
"type": "Action.OpenUrl",
"title": "Join Meeting",
"url": meeting.webLink
}
]
}
self.send_adaptive_card(room_id, meeting_card)
except Exception as e:
print(f"Meeting creation error: {e}")
self.send_message(room_id, "❌ Failed to create meeting")
# Flask Webhook Server
app = Flask(__name__)
bot = None
@app.route('/webhooks/messages', methods=['POST'])
def handle_message_webhook():
"""Message webhook processing"""
webhook_data = request.json
if webhook_data.get('event') == 'created':
bot.handle_message(webhook_data['data'])
return jsonify({'status': 'success'})
@app.route('/api/alert', methods=['POST'])
def handle_alert():
"""External system alert processing"""
alert_data = request.json
room_id = alert_data.get('roomId')
if not room_id:
return jsonify({'error': 'roomId required'}), 400
try:
alert_card = {
"type": "AdaptiveCard",
"version": "1.4",
"body": [
{
"type": "TextBlock",
"text": f"🚨 {alert_data.get('title', 'System Alert')}",
"weight": "Bolder",
"size": "Medium"
},
{
"type": "TextBlock",
"text": alert_data.get('description', ''),
"wrap": True
},
{
"type": "FactSet",
"facts": [
{"title": "Severity", "value": alert_data.get('severity', 'Unknown')},
{"title": "Service", "value": alert_data.get('service', 'Unknown')},
{"title": "Timestamp", "value": alert_data.get('timestamp', 'Unknown')}
]
}
]
}
bot.send_adaptive_card(room_id, alert_card)
return jsonify({'status': 'success'})
except Exception as e:
print(f"Alert send error: {e}")
return jsonify({'error': 'Failed to send alert'}), 500
@app.route('/health', methods=['GET'])
def health_check():
"""Health check"""
return jsonify({'status': 'healthy', 'timestamp': datetime.now().isoformat()})
def run_flask_app():
"""Run Flask app in separate thread"""
app.run(host='0.0.0.0', port=int(os.getenv('PORT', 3000)), debug=False)
if __name__ == '__main__':
access_token = os.getenv('WEBEX_ACCESS_TOKEN')
if not access_token:
print("❌ WEBEX_ACCESS_TOKEN not set")
exit(1)
# Bot initialization
bot = WebexPythonBot(access_token)
print("🚀 Webex Python Bot started")
# Start Flask server in separate thread
flask_thread = threading.Thread(target=run_flask_app)
flask_thread.daemon = True
flask_thread.start()
# Main thread stays alive
try:
while True:
time.sleep(1)
except KeyboardInterrupt:
print("🛑 Stopping bot")
Environment Variables Configuration
# .env file
# Webex API settings
WEBEX_ACCESS_TOKEN=your-webex-access-token
WEBEX_WEBHOOK_SECRET=your-webhook-secret
# Webhook URL (production environment)
WEBHOOK_URL=https://your-domain.com/webhooks
# GitHub integration
GITHUB_TOKEN=your-github-token
GITHUB_NOTIFICATION_ROOM=your-room-id
# External API integration
WEATHER_API_KEY=your-weather-api-key
# Server settings
PORT=3000
NODE_ENV=production
# Database (optional)
DATABASE_URL=your-database-url
# Cisco Webex Device integration (optional)
WEBEX_DEVICE_USERNAME=your-device-username
WEBEX_DEVICE_PASSWORD=your-device-password
# Enterprise settings
WEBEX_ORG_ID=your-organization-id
[email protected]
Docker Configuration Example
# Dockerfile
FROM node:18-alpine
WORKDIR /app
# Install dependencies
COPY package*.json ./
RUN npm ci --only=production
# Copy application files
COPY . .
# Environment variables
ENV NODE_ENV=production
ENV PORT=3000
# Expose port
EXPOSE 3000
# Health check
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD curl -f http://localhost:3000/health || exit 1
# Start application
CMD ["npm", "start"]
Docker Compose Configuration
# docker-compose.yml
version: '3.8'
services:
webex-bot:
build: .
container_name: webex-bot
restart: unless-stopped
ports:
- "3000:3000"
environment:
- WEBEX_ACCESS_TOKEN=${WEBEX_ACCESS_TOKEN}
- WEBEX_WEBHOOK_SECRET=${WEBEX_WEBHOOK_SECRET}
- WEBHOOK_URL=${WEBHOOK_URL}
- WEATHER_API_KEY=${WEATHER_API_KEY}
- GITHUB_TOKEN=${GITHUB_TOKEN}
- PORT=3000
volumes:
- ./logs:/app/logs
networks:
- webex-network
# Optional: Redis for session management
redis:
image: redis:7-alpine
container_name: webex-redis
restart: unless-stopped
volumes:
- redis_data:/data
networks:
- webex-network
# Optional: PostgreSQL for data storage
postgres:
image: postgres:15-alpine
container_name: webex-postgres
restart: unless-stopped
environment:
POSTGRES_DB: webex_bot
POSTGRES_USER: webex
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
volumes:
- postgres_data:/var/lib/postgresql/data
networks:
- webex-network
volumes:
redis_data:
postgres_data:
networks:
webex-network:
driver: bridge
CI/CD Pipeline Integration
# .github/workflows/webex-notification.yml
name: Webex Deployment Notification
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
notify-deployment:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Deploy to production
run: |
# Deployment process
echo "Deploying to production..."
- name: Notify Webex on success
if: success()
run: |
curl -X POST "${{ secrets.WEBEX_WEBHOOK_URL }}" \
-H "Content-Type: application/json" \
-d '{
"roomId": "${{ secrets.WEBEX_ROOM_ID }}",
"alertData": {
"title": "Deployment Successful",
"description": "Production deployment completed successfully",
"severity": "info",
"service": "GitHub Actions",
"timestamp": "'$(date -u +%Y-%m-%dT%H:%M:%SZ)'",
"dashboardUrl": "https://github.com/${{ github.repository }}/actions"
}
}'
- name: Notify Webex on failure
if: failure()
run: |
curl -X POST "${{ secrets.WEBEX_WEBHOOK_URL }}" \
-H "Content-Type: application/json" \
-d '{
"roomId": "${{ secrets.WEBEX_ROOM_ID }}",
"alertData": {
"title": "Deployment Failed",
"description": "Production deployment failed",
"severity": "critical",
"service": "GitHub Actions",
"timestamp": "'$(date -u +%Y-%m-%dT%H:%M:%SZ)'",
"dashboardUrl": "https://github.com/${{ github.repository }}/actions"
}
}'