Cisco Innovation Challenge | Behind The Scene

Too Long, Didn’t Read: I used the Webex App and platform API to create 12 rooms, assign 60+ people in the rooms, set up 43+ Webex Meetings, and add 12 co-hosts to those meetings for a four-day virtual event. A Webex bot guided people to the correct meetings.

What is this about?

On October 7,8 & 9, Cisco BeLux organised the second edition of the Cisco Innovation Challenge (CIC). The event is designed to allow customers to innovate on a (specific) problem statement they have with the help of Design Thinking methodologies. 

I had the privilege to join as a team coach and logistics facilitator as part of the organisation. When we shaped the form of the event last year, it was of course the purpose to host it at the Cisco campus in Diegem. But due to the current Covid-context we live in, we had to re-think the format of the event. 

At Cisco, we have all the collaboration tools (Webex Teams & Webex Meetings) available but how do you set up a four-day virtual event with 6 teams, general session, break-out sessions, …? And especially how do you let participants navigate through all those different meetings? 

HINT: API’s to the rescue

The concept

When I started to write down all the things I had to create, it was quite clear I didn’t want to this manually. Not that I ever was planning to do this manually but anyway…

First, let’s have a look at what my action list was.
Webex Teams
– Create a Team for the live event
– In that team, create 12 rooms (6 rooms for the participating teams, some for logistic issues, one for the organisation, one for the jury, …)
– Assign people (+60) to the right rooms with appropriate privileges (moderator or not)

Webex Meetings
– 4 opening meetings
– 4 closing meetings
– 24 breakout meetings
– 3 final team check out meetings
– 6 meetings for the coaches to come together
– 2 meetings for the jury briefing and jury deliberation
– 1 ‘learn to pitch’ meeting

Can you see where I’m going? Manually creating 12 rooms, adding people to the rooms, scheduling 43 meetings, adding co-hosts, creating Outlook meetings, … no thanks.
And this is just the creating & scheduling part, how do participants know which meeting to join? 

This is where a messaging bot can provide some value. I’ve created a bot so attendees can ask the agenda, with links to the Webex Meetings.

Ok, it’s a good start. But you need to know when you participate in a ‘hackathon’, there is a lot of stress and time pressure. So people could lose track of time and as a result not attending the correct meeting.

So I could leverage the bot for broadcasting messages with tailored made information to the teams so they know directly what meetings to attend.

Enough concepts. Let’s check out how you could implement this.

The implementation

Webex Teams

First, let’s create a bot that can do some automation for us. Creating a bot is plain simple. Fill in some variables and eventually, you’ll get a bearer token that allows you to make requests to the Webex Teams API.

Next, create a Team in Webex Teams manually and add your bot as moderator so it has the appropriate privileges to add rooms and people.

Done. Now it’s time to add some rooms and add members. Before we can do that, we will need to retrieve the ‘teamId’ for the team we’ve just created. Note that we’re doing the API requests from the bot’s perspective so it’s important you’ve added the bot to the team (and rooms) as moderator, as explained in the previous step.

Ok, simple, do a GET request to the Webex Teams Team API and the response will list all the teams the bots belongs to.

import requests
import json

WebExBearer = ''
WebExUrl = 'https://webexapis.com/v1/' 

def getHeader():
    return { 'Authorization': 'Bearer ' + WebExBearer, 'Content-Type': 'application/json' } 

response = requests.request("GET", WebExUrl + "teams", headers=getHeader())
response = json.loads(response.text)
for i in response['items']:
	print('Team name: ' + i['name'] + ' | TeamID: ' + i['id'])

You’ll end up with something like this:

Team name: LIVE: Cisco Innovation Challenge 2020 | TeamID: aaaaabbbbbbcccccccdddddddd

Now we have the Team ID, we can start creating rooms and room memberships. You could do this in one single script but I didn’t had the full list of room members yet so it’s easier to first create the rooms and add people later on.

You’ll need to post to the Webex Teams Rooms API. This request is really simple, just create a list with rooms with the names you want and loop over that list. Provide the ‘teamId’ we figured out in the previous step.

import requests
import json

WebExBearer = ''
teamId = ''
WebExUrl = 'https://webexapis.com/v1/' 

rooms = ['Logistic Issues', 'Organisation']

def getHeader():
    return { 'Authorization': 'Bearer ' + WebExBearer, 'Content-Type': 'application/json' } 

for i in rooms:
    a = {
       "title": i,
       "teamId": teamId,
    }
    
    payload = json.dumps(a)
    response = requests.request("POST", WebExUrl + "rooms", data=payload, headers=getHeader()   
    response = json.loads(response.text)
    print(response.text) Title / ID
    print('Room name: ' + i['title'] + ' | RoomID: ' + i['id'])

The script will output all the rooms with the room ID. Important you save those somewhere.

Room name: MC | RoomID: aaaabbbccccdddd
Room name: Logistic Issues | RoomID: aaaabbbccccdddd
Room name: Team - Test | RoomID: aaaabbbccccdddd

Good, we created all the rooms. Now, we need to add some people.

In the script below, I’ve created a list ‘vcic_teams’ with dictionaries. The dict consists of the team name (this has no purpose in the script itself but is a nice way to make sure you’re adding people in the right team). Next, you’ll need to add the roomId we saved in the previous step. Next, add an another list with the email addresses.

In this stage, I added my Cisco colleagues (team coaches) to the rooms as moderators (see the boolean ‘isModerator’).

Later, when I received the list of all the participants, I could easily add those to the scripts and change the boolean ‘isModerator’ to False.

No need to save the output of the script, just check that you receive a ‘200’ response code to make sure the people are added.

import requests
import json

WebExBearer = ''
WebExUrl = 'https://webexapis.com/v1/' 

vcic_teams = [{
  "team": "Logistic Issues",
  "webexRoomId": "aaaabbbccccdddd",
  "attendees": ['blabla@cisco.com','blabla@cisco.com']
}, {
  "team": "Organisation",
  "webexRoomId": "aaaabbbccccdddd",
  "attendees": ['blabla@cisco.com','blabla@cisco.com']
}]

def getHeader(bearer):
    return { 'Authorization': 'Bearer ' + bearer, 'Content-Type': 'application/json' } 

for i in vcic_teams:
    for j in i['attendees']:
        a = {
          "roomId": i['webexRoomId'],
          "personEmail": j,
          "isModerator": True
        }
        payload = json.dumps(a)
    
        response = requests.request("POST", WebExUrl + "memberships", data=payload, headers=getHeader(WebExBearer))   
        print(response.status_code)

Great, now you have created a Team with different rooms and added people to the different rooms. They can start sending GIF’s to each other 🙂

Webex Meetings

Next, time to set up those 43 Webex Meetings.

Well, this is also not rocket science. First, you’ll need to obtain your personal token, that’s valid for a limited time. There are some other methods available like giving your bot access to your WebEx account but this was not needed in my case.

There was a pattern in the meetings I had to create. For example, there was always an opening, closing, and breakout session for every day. So I created a list with the teams and a dict with the different dates.

So, if I wanted to create breakout Webex Meetings for every day for every team, I looped over the two lists. If I had to create the opening & closing sessions, I just had to loop over the day’s list. Of course, adjust the date/time appropriately and the name of the meeting.

I always saved the output so I could use that data later for integrating it in the bot and provide the Cisco Innovation Challenge organisation with all the meetings details (weblink, host key, passwords, meetingid, …) in case there would a be problem, which didn’t happen :).

Create a POST request to the Webex Meetings API.

import dateutil.parser as parser
import json
import requests

teams = [
    { "team": "Team1"},
    { "team": "Team2"},
    { "team": "Team3"},
    { "team": "Team4"},
    { "team": "Team5"},
    { "team": "Team6"}
]

days = [
    {'day': 0, 'start': '2 Oct 2020 10:00:00 +0200', 'end': '2 Oct 2020 11:30:00 +0200'},
    {'day': 1, 'start': '7 Oct 2020 10:00:00 +0200', 'end': '7 Oct 2020 16:30:00 +0200'},
    {'day': 2, 'start': '8 Oct 2020 10:00:00 +0200', 'end': '8 Oct 2020 16:30:00 +0200'},
    {'day': 3, 'start': '9 Oct 2020 10:00:00 +0200', 'end': '9 Oct 2020 16:30:00 +0200'}
]

meeting_list = []

for team in teams:
    for day in days: 
        data = {
            "title": team['team'] + ' - Day' + str(day['day']) + ' - Breakout',
            "password": "vcic2020",
            "timezone": "Europe/Brussels",
            "start": parser.parse(day['start']).isoformat(),
            "end": parser.parse(day['end']).isoformat(),
            "enabledAutoRecordMeeting": False,
            "allowAnyUserToBeCoHost": True,  
        }        
        data = json.dumps(data)  
        headers = { 'Authorization': "Bearer ", 'Content-Type': 'application/json' } 
        print(data)
        response = requests.request("POST", "https://webexapis.com/v1/meetings", data=data, headers=headers)
        json_data = json.loads(response.text)

        meeting = {}
        
        for key, value in json_data.items():
            if key == "title":
                meeting['title'] = value
            if key == "id":
                meeting['id'] = value                
            if key == "password":
                meeting['password'] = value
            if key == "webLink":
                meeting['webLink'] = value
            if key == "hostKey":
                meeting['hostKey'] = value

        meeting_list.append(meeting)
    
print(meeting_list)

Ok, I was able to create all the required meetings in a couple of minutes. That’s quick.

When you setup meetings, you’re the host of those meetings so I had to make sure I was able to assign co-hosts to the meetings. Luckily, this can also be achieved with the Webex Meeting Invitees API.

All I had to do, is to assign the right email addresses to the correct meetingID, information we just got from the previous step. I created a structure where I could loop over and assign the co-host(s) to a single or multiple meetingId’s.

import requests
import json

meetingId = [{
		"cohost": ["blabla@cisco.com", "blabla@cisco.com"],
		"meetingIds": ["aaaaabbbbbcccc", "aaaaabbbbbcccc"],
	},
	{
		"cohost": ["blabla@cisco.com"],
		"meetingIds": ["dddddeeeeeeffffff"]
	},
]

for i in meetingId:
	for host in i['cohost']:
		for mid in i['meetingIds']:
			a = {
					"email": host,
					"meetingId": mid,
					"coHost": True
			}

			data = json.dumps(a)
			headers = { 'Authorization': 'Bearer ', 'Content-Type': 'application/json' } 
			response = requests.request("POST", "https://webexapis.com/v1/meetingInvitees", data=data, headers=headers)
			print(response.status_code)

And done, all the co-hosts are assigned to the correct meetings.
Now, people can communicate with each other via Webex Teams and all the meetings are scheduled. It’s time to simplify the virtual experience by guiding them to the correct meetings.

The vCIC 2020 Bot

Time for the fun stuff. The vCIC 2020 bot was able to answer a few questions (what’s the agenda, what’s the Miro board). But it was also used to broadcast messages to the different rooms.

Broadcasting messages from the bot is extremely simple, asking a question to the bot is a bit more complex.

First, let’s discuss asking questions to the bot. When you ask a question to the bot, Webex Teams will forward the message ID to a web server you’ll need to setup. Then, your server will have to make a GET request to the Webex Teams API to retrieve the text message and then you can do something with that.

So, how to set up a webserver? You could build a simple Python Django web server, create a NodeJS server, … but I’ve used a ‘serverless’ (I hate the word) function in Google Cloud Platform. You just need to set up a function with, in my case, the python script and generate a HTTP(s) link to that function. You’ll need to link that webhook to your bot so Webex Teams knows where to send the messageId.

def webhookListener(request):
    
    body = json.loads(request.data)
    if 'data' in body:
        messageId = body['data']['id']
        user_id = body["data"]["personId"]
        if user_id != WebExBotID:
            getMessage(messageId)

    return json.dumps({'success':True}), 200, {'ContentType':'application/json'} 

Now, whenever Webex Teams receives a message from the bot, I could parse the messageId and pass it to another function (getMessage) in my ‘serverless’ app that receives the plain text written by the user and the roomId it came from. Then, I could post a message back to that room.

In my application, I had a huge list with all the general, closing, breakout meetings, .. per team. I know, it’s not sexy, but I didn’t have the time to create a small database for it. I promise I’ll do that next time 🙂

By having that huge list, I could then define what Webex Meetings were linked to a specific room and provide that information back to the room.

import requests
import json

WebExBotName = 'vCIC 2020'
WebExBearer = ''
WebExBotID = ''
vcic_teams=[] ## This was the huge list

WebExUrl = 'https://webexapis.com/v1/'

# Function to return header
def getHeader(bearer):
    return { 'Authorization': "Bearer " + bearer } 

    
# POST call for post message in WebEx Teams Room
def postWebEx(message, roomId):
    payload = {
        "roomId": roomId,
        "markdown": message
    }   
    
    response = requests.request("POST", WebExUrl + "messages/", data=payload, headers=getHeader(WebExBearer))    

# Get message if question have been asked to the bot
def getMessage(messageId):
    url = WebExUrl + "messages/" + str(messageId)
    response = requests.request("GET", url, headers=getHeader(WebExBearer))
    message = "Hmm, I'm currently not able to answer this. Ask my master to implement this."

    json_data = json.loads(response.text)   
    cleartext = json_data['text']
    roomId = json_data['roomId']
    cleartext = cleartext.replace(WebExBotName, '')
    cleartext = cleartext.lower()

    if 'mi' in cleartext or 'meet' in cleartext or 'ag' in cleartext:
        for team in vcic_teams:
            if team['webexRoomId'] == roomId:
                if 'mi' in cleartext:
                    message = "[Here](" + team['MiroUrl'] + ") is the link to the " + team['team'] + " Miro board."
                if 'meet' in cleartext or 'ag' in cleartext:
                    meetings = ['Below are the scheduled WebEx meetings for team **' + team['team'] + '**.' ]
                    for i in team['meetings']:
                      for key, value in i.items():
                        meetings.append("\nMeetings on **" + key + "**:\n")
                        for val in value:
                          meet = '- ' + val['title'] + ' | Password: ' + val['password'] + ' | **[Join the meeting](' + val['webLink'] + ')**'
                          meetings.append(meet)
                    message = '\n '.join([str(elem) for elem in meetings]) 
                    
    elif 'help' in cleartext:
        message = "Thanks for asking assistance! You can ask me:\n - @vCIC 2020 meeting or @vCIC 2020 agenda: This will list all the agenda & scheduled meetings\n - @vCIC 2020 miro: This will provide the Miro Board link"

    postWebEx(message, roomId)

So, if you now asked the agenda to the bot, it would reply with the meetings links for that specific team.

Done. Next, let’s talk about the broadcast messages.

I created a list with different dicts that included the WebEx RoomId and breakout session. So I could send out the general opening & closing session links (that are the same for everyone) but also the specific break-out Webex meeting links, specific for that team.

Then I had some fun writing text messages that would be sent through the different days. I first was thinking about scheduling them but I’m happy I didn’t do that as opening and closing meetings sometimes went over time.

Ok, I had to think about sending out those messages ‘manually’ during the day but that went fine. It’s just (un)commenting a specific message and executing the script.

import requests
import json

vcic_teams = [
{
  "team": "Team 1",
  "webexRoomId": "blablabla",
  "breakout": "\n **[Join the breakout](https://cisco.webex.com/cisco-du/j.php?MTID=blablabla)** (password: blablabla).",
  "checkout": "\n **[Join the team check-out](https://cisco.webex.com/cisco-du/j.php?MTID=blablabla)** (password: blablabla)."

}, {
  "team": "Team 2",
  "webexRoomId": "blablabla",
  "breakout": "\n **[Join the breakout](https://cisco.webex.com/cisco-du/j.php?MTID=blablabla)** (password: blablabla).",
  "checkout": "\n **[Join the team check-out](https://cisco.webex.com/cisco-du/j.php?MTID=blablabla)** (password: blablabla)."
}]

WebExBearer = ''
WebExUrl = 'https://webexapis.com/v1/'

def getHeader(bearer):
    return { 'Authorization': "Bearer " + bearer } 

for i in vcic_teams:

    # DONE: message = "Whaaaat? Is it the final day already? :( Ooh yes, it is! \n At 09h00, **[join the opening](https://cisco.webex.com/cisco-du/j.php?MTID=blabla)** (password: blabla)."
    # DONE: message = "Oh my... I still feel a bit stressed. No worries, you still have time to develop that solution! Go Go Go!" + i['breakout']
    # DONE: message = "Don't forget to **grab some lunch**, you need energy :)"
    # DONE: message = "Not sure what you should do now... Hmmmm... Still thinking... GOT IT! **Record that perfect PITCH**!"
    # DONE: message = "WOW, you've done it, the pitch is recorded! **Congrats!** Now, take a coffee and prepare for those nasty jury questions :)"
    # DONE: message = "It's finally time to **'relax' a bit and listen to the pitches**. At 15h00, **[join the general session](https://cisco.webex.com/cisco-du/j.php?MTID= blabla)** (password: blabla)."
    # DONE: message = "The jury can't think as fast I can write messages to you :) Let's give them some time to deliberate. Meanwhile, let's do a final team check-out." + i['checkout']
    # DONE: message = "Damn, that was some hard decision making but it's done :) **[Join the celebration and closing now](https://cisco.webex.com/cisco-du/j.php?MTID= blabla)** (password: blabla)."

    message = "My master asked me to go to sleep now :( I hope you enjoyed the Cisco Innovation Challenge as much as I did. \n Again, many many many **congratulations**, see you next time and enjoy your well deserved weekend!"


    payload = {
        "roomId": i['webexRoomId'],
        "markdown": message
    }   

    response = requests.request("POST", WebExUrl + "messages/", data=payload, headers=getHeader(WebExBearer))    

An example of the messages that were broadcasted:

The bot itself got a lot of positive feedback as it’s simplified the virtual experience of the attendees. And it’s a fun and easy way to broadcast information.

Wrap up

If you’re planning to host a virtual event as we did, you would want to automate the creation of the communication platform and meetings. By providing a ‘fun’ bot, you simplify the virtual experience and allow the attendees to navigate through the agenda seamlessly.

All information about the Webex Meetings & Teams API can be found on https://developer.webex.com.

Have fun!

Leave A Comment

Your email address will not be published. Required fields are marked *