How to attend two meeting room at the same time?

I am creating a meeting bot that records a conference room by referring to the following example.
https://github.com/zoom/meetingsdk-linux-raw-recording-sample

I succeeded in obtaining raw voice data by joining the conference room with the code.

I want to create a bot as an instance unit on one server and connect it to multiple conference rooms at the same time.

For this, I created a ZoomSession and separated the variables as shown in the code below, but the bot failed to join more than one conference room.

class ZoomSDKSession {
public:
IAuthService* authService;
IMeetingService* meetingService;
ISettingService* settingService;

// Audio related members
std::unique_ptr<ZoomSDKAudioRawData> audioSource;
IZoomSDKAudioRawDataHelper* audioHelper;

// Video related members
std::unique_ptr<ZoomSDKRenderer> videoSource;
IZoomSDKRenderer* videoHelper;

ZoomSDKSession(): authService(nullptr), meetingService(nullptr), settingService(nullptr) {} };

... 
    session->audioSource = std::make_unique<ZoomSDKAudioRawData>(session->meetingService);
    session->videoSource = std::make_unique<ZoomSDKRenderer>();

If the bot first joins a meeting room and then tries to join another meeting room, it fails with the following:

Leaving recording token as NULL
join_meeting:error 2 (SDKERR_WRONG_USAGE,///<Incorrect usage of the feature. 
)
Incorrect usage of the feature

The action creates a Session before joining a meeting room and creates services, audio_source, audio_helper, etc.

Please help me solve this.

my code is

// C++ Standard Library
#include <string>
#include <iostream>
#include <sstream>
#include <fstream>
#include <thread>
#include <map>
#include <algorithm>
#include <numeric>
#include <mutex>

// System Headers
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/syscall.h>

// External Libraries
#include <glib.h>
#include <asio/io_service.hpp>

// Zoom SDK Core
#include "zoom_sdk.h"
#include "auth_service_interface.h"
#include "meeting_service_interface.h"

// Zoom Meeting Components
#include "meeting_service_components/meeting_audio_interface.h"
#include "meeting_service_components/meeting_participants_ctrl_interface.h"
#include "meeting_service_components/meeting_video_interface.h"
#include "meeting_service_components/meeting_recording_interface.h"

// Zoom Settings
#include "setting_service_interface.h"

// Custom Services
#include "MeetingJoinTokenService.h"
#include "WebsocketServer.h"
#include "RestServer.h"
#include "ZoomSDKManager.h"

// Event Listeners
#include "MeetingReminderEventListener.h"
#include "MeetingServiceEventListener.h"
#include "AuthServiceEventListener.h"
#include "MeetingParticipantsCtrlEventListener.h"
#include "MeetingRecordingCtrlEventListener.h"

// Raw Data Handlers
#include "ZoomSDKRenderer.h"
#include "ZoomSDKAudioRawData.h"
#include "ZoomSDKVideoSource.h"
#include "ZoomSDKVirtualAudioMicEvent.h"
#include "rawdata/rawdata_renderer_interface.h"
#include "rawdata/zoom_rawdata_api.h"

// Network Handlers
#include "NetworkConnectionHandler.h"

//The port number the WebSocket server listens on
#define PORT_NUMBER 8080

USING_ZOOM_SDK_NAMESPACE


//references for SendAudioRawData
std::string DEFAULT_AUDIO_SOURCE = "yourwavefile.wav";

//references for SendVideoRawData
std::string DEFAULT_VIDEO_SOURCE = "yourmp4file.mp4";


GMainLoop* loop;


//These are needed to readsettingsfromTEXT named config.txt
std::string recording_token;



//this is used to get a userID, there is no specific proper logic here. It just gets the first userID.
//userID is needed for video subscription.
unsigned int userID;


//this will enable or disable logic to get raw video and raw audio
//do note that this will be overwritten by config.txt
bool GetVideoRawData = false;
bool GetAudioRawData = true;
bool SendVideoRawData = false;
bool SendAudioRawData = false;

std::string g_meeting_number;
std::string g_meeting_password;

void printSDKError(SDKError err) {
	switch(err) {
			case ZOOM_SDK_NAMESPACE::SDKERR_SUCCESS:
				std::cout << "Success" << std::endl;
				break;
			case ZOOM_SDK_NAMESPACE::SDKERR_NO_IMPL:
				std::cout << "This feature is currently invalid" << std::endl;
				break;
			case ZOOM_SDK_NAMESPACE::SDKERR_WRONG_USAGE:
				std::cout << "Incorrect usage of the feature" << std::endl;
				break;
			case ZOOM_SDK_NAMESPACE::SDKERR_INVALID_PARAMETER:
				std::cout << "Wrong parameter" << std::endl;
				break;
			case ZOOM_SDK_NAMESPACE::SDKERR_MODULE_LOAD_FAILED:
				std::cout << "Loading module failed" << std::endl;
				break;
			case ZOOM_SDK_NAMESPACE::SDKERR_MEMORY_FAILED:
				std::cout << "No memory is allocated" << std::endl;
				break;
			case ZOOM_SDK_NAMESPACE::SDKERR_SERVICE_FAILED:
				std::cout << "Internal service error" << std::endl;
				break;
			case ZOOM_SDK_NAMESPACE::SDKERR_UNINITIALIZE:
				std::cout << "Not initialized before the usage" << std::endl;
				break;
			case ZOOM_SDK_NAMESPACE::SDKERR_UNAUTHENTICATION:
				std::cout << "Not authorized before the usage" << std::endl;
				break;
			case ZOOM_SDK_NAMESPACE::SDKERR_NORECORDINGINPROCESS:
				std::cout << "No recording in process" << std::endl;
				break;
			case ZOOM_SDK_NAMESPACE::SDKERR_TRANSCODER_NOFOUND:
				std::cout << "Transcoder module is not found" << std::endl;
				break;
			case ZOOM_SDK_NAMESPACE::SDKERR_VIDEO_NOTREADY:
				std::cout << "The video service is not ready" << std::endl;
				break;
			case ZOOM_SDK_NAMESPACE::SDKERR_NO_PERMISSION:
				std::cout << "No permission" << std::endl;
				break;
			case ZOOM_SDK_NAMESPACE::SDKERR_UNKNOWN:
				std::cout << "Unknown error" << std::endl;
				break;
			case ZOOM_SDK_NAMESPACE::SDKERR_OTHER_SDK_INSTANCE_RUNNING:
				std::cout << "The other instance of the SDK is in process" << std::endl;
				break;
			case ZOOM_SDK_NAMESPACE::SDKERR_INTERNAL_ERROR:
				std::cout << "SDK internal error" << std::endl;
				break;
			case ZOOM_SDK_NAMESPACE::SDKERR_NO_AUDIODEVICE_ISFOUND:
				std::cout << "No audio device found" << std::endl;
				break;
			case ZOOM_SDK_NAMESPACE::SDKERR_NO_VIDEODEVICE_ISFOUND:
				std::cout << "No video device found" << std::endl;
				break;
			case ZOOM_SDK_NAMESPACE::SDKERR_TOO_FREQUENT_CALL:
				std::cout << "API calls too frequently" << std::endl;
				break;
			case ZOOM_SDK_NAMESPACE::SDKERR_FAIL_ASSIGN_USER_PRIVILEGE:
				std::cout << "User can't be assigned with new privilege" << std::endl;
				break;
			case ZOOM_SDK_NAMESPACE::SDKERR_MEETING_DONT_SUPPORT_FEATURE:
				std::cout << "The current meeting doesn't support the feature" << std::endl;
				break;
			case ZOOM_SDK_NAMESPACE::SDKERR_MEETING_NOT_SHARE_SENDER:
				std::cout << "The current user is not the presenter" << std::endl;
				break;
			case ZOOM_SDK_NAMESPACE::SDKERR_MEETING_YOU_HAVE_NO_SHARE:
				std::cout << "There is no sharing" << std::endl;
				break;
			case ZOOM_SDK_NAMESPACE::SDKERR_MEETING_VIEWTYPE_PARAMETER_IS_WRONG:
				std::cout << "Incorrect ViewType parameters" << std::endl;
				break;
			case ZOOM_SDK_NAMESPACE::SDKERR_MEETING_ANNOTATION_IS_OFF:
				std::cout << "Annotation is disabled" << std::endl;
				break;
			case ZOOM_SDK_NAMESPACE::SDKERR_SETTING_OS_DONT_SUPPORT:
				std::cout << "Current OS doesn't support the setting" << std::endl;
				break;
			case ZOOM_SDK_NAMESPACE::SDKERR_EMAIL_LOGIN_IS_DISABLED:
				std::cout << "Email login is disabled" << std::endl;
				break;
			case ZOOM_SDK_NAMESPACE::SDKERR_HARDWARE_NOT_MEET_FOR_VB:
				std::cout << "Computer doesn't meet the minimum requirements to use virtual background feature" << std::endl;
				break;
			case ZOOM_SDK_NAMESPACE::SDKERR_NEED_USER_CONFIRM_RECORD_DISCLAIMER:
				std::cout << "Need process disclaimer" << std::endl;
				break;
			case ZOOM_SDK_NAMESPACE::SDKERR_NO_SHARE_DATA:
				std::cout << "There is no raw data of sharing" << std::endl;
				break;
			case ZOOM_SDK_NAMESPACE::SDKERR_SHARE_CANNOT_SUBSCRIBE_MYSELF:
				std::cout << "Cannot subscribe to own share" << std::endl;
				break;
			case ZOOM_SDK_NAMESPACE::SDKERR_NOT_IN_MEETING:
				std::cout << "Not in meeting" << std::endl;
				break;
			case ZOOM_SDK_NAMESPACE::SDKERR_NOT_JOIN_AUDIO:
				std::cout << "Not joined audio" << std::endl;
				break;
			case ZOOM_SDK_NAMESPACE::SDKERR_HARDWARE_DONT_SUPPORT:
				std::cout << "The current device doesn't support the feature" << std::endl;
				break;
			case ZOOM_SDK_NAMESPACE::SDKERR_DOMAIN_DONT_SUPPORT:
				std::cout << "Domain doesn't support" << std::endl;
				break;
			case ZOOM_SDK_NAMESPACE::SDKERR_MEETING_REMOTE_CONTROL_IS_OFF:
				std::cout << "Remote control is disabled" << std::endl;
				break;
			default:
				std::cout << "Other error occurred" << std::endl;
		}
}

//this is a helper method to get the first User ID, it is just an arbitary UserID
uint32_t getUserID(std::string meeting_number) {
	auto& manager = ZoomSDKManager::getInstance();
	auto session = manager.getSession(meeting_number);

	auto participantsController = session->meetingService->GetMeetingParticipantsController();
	int returnvalue = participantsController->GetParticipantsList()->GetItem(0);
	std::cout << "UserID is : " << returnvalue << std::endl;
	return returnvalue;
}

IUserInfo* getMyself(std::string meeting_number) {
	auto& manager = ZoomSDKManager::getInstance();
	auto session = manager.getSession(meeting_number);
	auto participantsController = session->meetingService->GetMeetingParticipantsController();
	IUserInfo* returnvalue = participantsController->GetMySelfUser();

	return returnvalue;
}

//this is a helper method to get the first User Object, it is just an arbitary User Object
IUserInfo* getUserObj(std::string meeting_number) {
	auto& manager = ZoomSDKManager::getInstance();
	auto session = manager.getSession(meeting_number);
	auto participantsController = session->meetingService->GetMeetingParticipantsController();
	int userID = participantsController->GetParticipantsList()->GetItem(0);
	IUserInfo* returnvalue = participantsController->GetUserByUserID(userID);
	std::cout << "UserID is : " << returnvalue << std::endl;
	return returnvalue;
}

//check if you have permission to start raw recording
void CheckAndStartRawRecording(bool isVideo, bool isAudio, std::string meeting_number) {

	auto& manager = ZoomSDKManager::getInstance();
	auto session = manager.getSession(meeting_number);

	if (isVideo || isAudio) {
		auto recordController = session->meetingService->GetMeetingRecordingController();
		SDKError err2 = recordController->CanStartRawRecording();

		if (err2 == SDKERR_SUCCESS) {
			SDKError err1 = recordController->StartRawRecording();
			std::cout << "StartRawRecording : " << err1 << std::endl;
			if (err1 != SDKERR_SUCCESS) {
				std::cout << "Error occurred starting raw recording" << std::endl;
			}
			else {
				//GetAudioRawData
				if (isAudio) {
					session->audioHelper = GetAudioRawdataHelper();
					if (session->audioHelper && session->audioSource) {
						SDKError err = session->audioHelper->subscribe(session->audioSource.get());
						if (err != SDKERR_SUCCESS) {
							std::cout << "Error occurred subscribing to audio : " << err << std::endl;
						}
					}
					else {
						std::cout << "Error getting audioHelper" << std::endl;
					}
				}
			}
		}
		else {
			std::cout << "Cannot start raw recording: no permissions yet, need host, co-host, or recording privilege" << std::endl;
		}
	}
}

//callback when given host permission
void onIsHost(std::string meeting_number) {
	printf("Is host now...\n");
	CheckAndStartRawRecording(GetVideoRawData, GetAudioRawData, meeting_number);
}

//callback when given cohost permission
void onIsCoHost(std::string meeting_number) {
	printf("Is co-host now...\n");
	CheckAndStartRawRecording(GetVideoRawData, GetAudioRawData, meeting_number);
}
//callback when given recording permission
void onIsGivenRecordingPermission(std::string meeting_number) {
	printf("Is given recording permissions now...\n");
	CheckAndStartRawRecording(GetVideoRawData, GetAudioRawData, meeting_number);
}


//callback when the SDK is inmeeting
void onInMeeting(std::string meeting_number) {

	std::cout<<"onInMeeting Invoked : " << meeting_number <<std::endl;

	auto& manager = ZoomSDKManager::getInstance();
	auto session = manager.getSession(meeting_number);
	MeetingStatus status = session->meetingService->GetMeetingStatus();

	std::cout<<status<<std::endl;

	switch (status)
	{
		case MEETING_STATUS_IDLE:
			printf("No meeting is running.\n");
			break;
		case MEETING_STATUS_CONNECTING:
			printf("Connect to the meeting server status.\n");
			break;
		case MEETING_STATUS_WAITINGFORHOST:
			printf("Waiting for the host to start the meeting.\n");
			break;
		case MEETING_STATUS_INMEETING:
			printf("onMeetingStatusChanged() In Meeting.\n");
			// if (onInMeeting_) onInMeeting_(meeting_number_);
			break;
		case MEETING_STATUS_DISCONNECTING:
			printf("Disconnect the meeting server, leave meeting status.\n");
			break;
		case MEETING_STATUS_RECONNECTING:
			printf("Reconnecting meeting server status\n");
			break;
		case MEETING_STATUS_FAILED:
			printf("Failed to connect the meeting server.\n");
			break;
		case MEETING_STATUS_ENDED:
			printf("Meeting ends.\n");
			// if (onMeetingEnds_) onMeetingEnds_(meeting_number_);
			break;
		case MEETING_STATUS_UNKNOW:
			printf("Unknown status.\n");
			break;
		case MEETING_STATUS_LOCKED:
			printf("Meeting is locked to prevent the further participants to join the meeting.\n");
			break;
		case MEETING_STATUS_UNLOCKED:
			printf("Meeting is open and participants can join the meeting.\n");
			break;
		case MEETING_STATUS_IN_WAITING_ROOM:
			printf("Participants who join the meeting before the start are in the waiting room.\n");
			break;
		default:
			printf("other");
			break;

	}
	//double check if you are in a meeting
	if (status == ZOOM_SDK_NAMESPACE::MEETING_STATUS_INMEETING) {
		printf("In Meeting Now...\n");

		//print all list of participants
		IList<unsigned int>* participants = session->meetingService->GetMeetingParticipantsController()->GetParticipantsList();
		
		for(int i = 0; i < participants->GetCount(); i++){
			IUserInfo* user = session->meetingService->GetMeetingParticipantsController()->GetUserByUserID(participants->GetItem(i));
			printf("[User %d]\n", i);
			printf("id: %d, name: %s\n", user->GetUserID(), user->GetUserName());
		}

		printf("Participants count: %d\n", participants->GetCount());

		// request local recoding
		auto recordController = session->meetingService->GetMeetingRecordingController();
		recordController->RequestLocalRecordingPrivilege();
	}

	//first attempt to start raw recording  / sending, upon successfully joined and achieved "in-meeting" state.
	CheckAndStartRawRecording(GetVideoRawData, GetAudioRawData, meeting_number);
	// CheckAndStartRawSending(SendVideoRawData, SendAudioRawData);

}

//on meeting ended, typically by host, do something here. it is possible to reuse this SDK instance
void onMeetingEndsQuitApp(std::string meeting_number) {
	// CleanSDK();
	//std::exit(0);
}

void onMeetingJoined(std::string meeting_number) {

	std::cout << "Joining Meeting("<<meeting_number<<")... "<<std::endl;
}


std::string getMeetingToken(std::string meeting_number)
{
	
	//meeting_number 
	MeetingJoinTokenService& meetingJoinTokenService = MeetingJoinTokenService::GetInstance();
	std::string token = meetingJoinTokenService.getToken(meeting_number);
	return token;
}


void CleanSDK()
{
	ZOOM_SDK_NAMESPACE::SDKError err(ZOOM_SDK_NAMESPACE::SDKERR_SUCCESS);

	ZoomSDKManager::getInstance().cleanup();

	//attempt to clean up SDK
	err = ZOOM_SDK_NAMESPACE::CleanUPSDK();
	if (err != ZOOM_SDK_NAMESPACE::SDKERR_SUCCESS)
	{
		std::cerr << "CleanSDK meetingSdk:error " << std::endl;
	}
	else
	{
		std::cerr << "CleanSDK meetingSdk:success" << std::endl;
	}
}


void JoinMeeting(std::string meeting_number, std::string meeting_password)
{
	std::cerr << "Joining Meeting" << std::endl;
	SDKError err2(SDKError::SDKERR_SUCCESS);
	auto& manager = ZoomSDKManager::getInstance();
	auto session = manager.getSession(meeting_number);

    session->audioSource = std::make_unique<ZoomSDKAudioRawData>(session->meetingService);
    session->videoSource = std::make_unique<ZoomSDKRenderer>();

	std::cerr << "Settingservice created." << std::endl;

	// Set the event listener for meeting status
	session->meetingService->SetEvent(new MeetingServiceEventListener(&onMeetingJoined, &onMeetingEndsQuitApp, &onInMeeting, meeting_number));

	// Set the event listener for host, co-host 
	auto participantsController = session->meetingService->GetMeetingParticipantsController();
	participantsController->SetEvent(new MeetingParticipantsCtrlEventListener(&onIsHost, &onIsCoHost, meeting_number));

	// Set the event listener for recording privilege status
	auto recordController = session->meetingService->GetMeetingRecordingController();
	recordController->SetEvent(new MeetingRecordingCtrlEventListener(&onIsGivenRecordingPermission, meeting_number));


	// set event listnener for prompt handler 
	IMeetingReminderController* meetingremindercontroller = session->meetingService->GetMeetingReminderController();
	MeetingReminderEventListener* meetingremindereventlistener = new MeetingReminderEventListener();
	meetingremindercontroller->SetEvent(meetingremindereventlistener);

	//prepare params used for joining meeting
	ZOOM_SDK_NAMESPACE::JoinParam joinParam;
	ZOOM_SDK_NAMESPACE::SDKError err(ZOOM_SDK_NAMESPACE::SDKERR_SERVICE_FAILED);
	joinParam.userType = ZOOM_SDK_NAMESPACE::SDK_UT_WITHOUT_LOGIN;
	ZOOM_SDK_NAMESPACE::JoinParam4WithoutLogin& withoutloginParam = joinParam.param.withoutloginuserJoin;
	// withoutloginParam.meetingNumber = 1231231234;
	withoutloginParam.meetingNumber = std::stoull(meeting_number);
	withoutloginParam.vanityID = NULL;
	withoutloginParam.userName = "Bot";
	// withoutloginParam.psw = "1";
	withoutloginParam.psw = meeting_password.c_str();
	withoutloginParam.customer_key = NULL;
	withoutloginParam.webinarToken = NULL;
	withoutloginParam.isVideoOff = false;
	withoutloginParam.isAudioOff = false;

	// std::cerr << "JWT token is " << token << std::endl;
	std::cerr << "Recording token is " << recording_token << std::endl;

	//automatically set app_privilege token if it is present in config.txt, or retrieved from web service
	withoutloginParam.app_privilege_token = NULL;
	if (!recording_token.size() == 0)
	{
		withoutloginParam.app_privilege_token = recording_token.c_str();
		std::cerr << "Setting recording token" << std::endl;
	}
	else
	{
		withoutloginParam.app_privilege_token = NULL;
		std::cerr << "Leaving recording token as NULL" << std::endl;
	}

	MeetingStatus status = session->meetingService->GetMeetingStatus();

	std::cout<<"Meeting status : " << status << std::endl;

	if (GetAudioRawData) {
		//set join audio to true
		ZOOM_SDK_NAMESPACE::IAudioSettingContext* pAudioContext = session->settingService->GetAudioSettings();
		if (pAudioContext)
		{
			//ensure auto join audio
			pAudioContext->EnableAutoJoinAudio(true);
		}
	}
	if (SendVideoRawData) {

		//ensure video is turned on
		withoutloginParam.isVideoOff = false;
		//set join video to true
		ZOOM_SDK_NAMESPACE::IVideoSettingContext* pVideoContext = session->settingService->GetVideoSettings();
		if (pVideoContext)
		{
			pVideoContext->EnableAutoTurnOffVideoWhenJoinMeeting(false);
		}
	}
	if (SendAudioRawData) {

		ZOOM_SDK_NAMESPACE::IAudioSettingContext* pAudioContext = session->settingService->GetAudioSettings();
		if (pAudioContext)
		{
			//ensure auto join audio
			pAudioContext->EnableAutoJoinAudio(true);
			pAudioContext->EnableAlwaysMuteMicWhenJoinVoip(true);
			pAudioContext->SetSuppressBackgroundNoiseLevel(Suppress_BGNoise_Level_None);

		}
	}


	//attempt to join meeting
	if (session->meetingService)
	{
		err = session->meetingService->Join(joinParam);
	}
	else
	{
		std::cout << "join_meeting m_pMeetingService:Null" << std::endl;
	}

	if (ZOOM_SDK_NAMESPACE::SDKERR_SUCCESS == err)
	{
		std::cout << "join_meeting:success" << std::endl;
	}
	else
	{
		std::cout << "join_meeting:error " << err << std::endl;
		manager.removeSession(meeting_number);
		printSDKError(err);
	}


	// g_meeting_number = "";
	// g_meeting_password = "";
}

//callback when authentication is compeleted
void OnAuthenticationComplete()
{
	std::cout << "OnAuthenticationComplete number:" << g_meeting_number << " / password: " << g_meeting_password << std::endl;
	JoinMeeting(g_meeting_number, g_meeting_password);
}


void AuthMeetingSDK(std::string meeting_number, std::string meeting_password)
{
	auto& manager = ZoomSDKManager::getInstance();

	
    if (!manager.createSession(meeting_number)) {
        std::cerr << "Failed to create session for meeting " << meeting_number << std::endl;
        return;
    }

    auto session = manager.getSession(meeting_number);
    if (!session) {
        std::cerr << "Failed to get session for meeting " << meeting_number << std::endl;
        return;
    }
	
	SDKError err(SDKError::SDKERR_SUCCESS);
	std::string token = getMeetingToken(meeting_number);

	//create auth service
	// if ((err = CreateAuthService(&m_pAuthService)) != SDKError::SDKERR_SUCCESS) {};
	// std::cerr << "AuthService created." << std::endl;

	g_meeting_number = meeting_number;
    g_meeting_password = meeting_password;

	//Create a param to insert jwt token
	ZOOM_SDK_NAMESPACE::AuthContext param;
	
	// Origin
	if ((err = session->authService->SetEvent(new AuthServiceEventListener(&OnAuthenticationComplete))) != SDKError::SDKERR_SUCCESS) {
        std::cerr << "Failed to set event for auth service result callback" << meeting_number << std::endl;
	} else {
		std::cout<< "Success set event for auth service result callback" <<std::endl;
	};
	if (!token.size() == 0){
		param.jwt_token = token.c_str();
		std::cerr << "AuthSDK:token generate : " << param.jwt_token  << std::endl;
	}
	session->authService->SDKAuth(param);
	std::cout<< "Success SDKAuth" <<std::endl;
}

void InitMeetingSDK()
{
	ZOOM_SDK_NAMESPACE::SDKError err(ZOOM_SDK_NAMESPACE::SDKERR_SUCCESS);
	ZOOM_SDK_NAMESPACE::InitParam initParam;

	// set domain
	initParam.strWebDomain = "https://zoom.us";
	initParam.strSupportUrl = "https://zoom.us";

	// set language id
	initParam.emLanguageID = ZOOM_SDK_NAMESPACE::LANGUAGE_English;

	//set logging perferences
	initParam.enableLogByDefault = true;
	initParam.enableGenerateDump = true;

	// attempt to initialize
	err = ZOOM_SDK_NAMESPACE::InitSDK(initParam);
	if (err != ZOOM_SDK_NAMESPACE::SDKERR_SUCCESS){
		std::cerr << "Init meetingSdk:error " << std::endl;
	}
	else{
		std::cerr << "Init meetingSdk:success" << std::endl;
	}
}

// Define a struct to hold the response data
struct ResponseData {
	std::ostringstream stream;
};

// Callback function to write response data into the stringstream
static size_t WriteCallback(void* contents, size_t size, size_t nmemb, void* userp) {
	size_t totalSize = size * nmemb;
	ResponseData* response = static_cast<ResponseData*>(userp);
	response->stream.write(static_cast<const char*>(contents), totalSize);
	return totalSize;
}


gboolean timeout_callback(gpointer data)
{
	return TRUE;
}

//this catches a break signal, such as Ctrl + C
void my_handler(int s)
{
	printf("\nCaught signal %d\n", s);
	// LeaveMeeting();
	printf("Leaving session.\n");
	CleanSDK();

	WebsocketServer::getInstance().stop();
	RestServer::getInstance().stop();
	//InitMeetingSDK();
	//AuthMeetingSDK();

	std::exit(0);
}

void initAppSettings()
{
	struct sigaction sigIntHandler;
	sigIntHandler.sa_handler = my_handler;
	sigemptyset(&sigIntHandler.sa_mask);
	sigIntHandler.sa_flags = 0;
	sigaction(SIGINT, &sigIntHandler, NULL);
}

void setupWebsocketServer(WebsocketServer& server, asio::io_service& io_service)
{
	server.connect([&io_service, &server](ClientConnection conn)
	{
		io_service.post([conn, &server]()
		{
			std::clog << "Connection opened." << std::endl;
			std::clog << "There are now " << server.numConnections() << " open connections." << std::endl;

			Json::Value jsonData;
			jsonData["userName"] = "bot";
			jsonData["text"] = "hello";
			server.sendMessage(conn, "message", jsonData);
		});
	});

	server.disconnect([&io_service, &server](ClientConnection conn)
	{
		io_service.post([conn, &server]()
		{
			std::clog << "Connection closed." << std::endl;
			std::clog << "There are now " << server.numConnections() << " open connections." << std::endl;
		});
	});

	server.message("message", [&io_service, &server](ClientConnection conn, const Json::Value& args)
	{
		io_service.post([conn, args, &server]()
		{
			std::clog << "message handler on the main thread" << std::endl;
			std::clog << "Message payload:" << std::endl;
			for (auto key : args.getMemberNames()) {
				std::clog << "\t" << key << ": " << args[key].asString() << std::endl;
			}
			
			server.sendMessage(conn, "message", args);
			std::clog << "numConnections : " << server.numConnections() << std::endl;
		});
	});
}

void inviteCallback(std::string meeting_number, std::string password) {
	std::cout << "Default join meeting callback - Meeting: " << meeting_number << " / password : " << password << std::endl;    


	AuthMeetingSDK(meeting_number, password);

}

int main(int argc, char* argv[])
{
	InitMeetingSDK();
	initAppSettings();

	asio::io_service io_service;
	asio::io_service::work work(io_service);

	WebsocketServer& server = WebsocketServer::getInstance();
	setupWebsocketServer(server, io_service);
	

	server.run(PORT_NUMBER);

	

    RestServer& restServer = RestServer::getInstance();
    restServer.addEndpoints();  
	restServer.addInviteEndpoint(inviteCallback);
    restServer.start();  


	loop = g_main_loop_new(NULL, FALSE);
	g_timeout_add(0, [](gpointer user_data) -> gboolean {
		asio::io_service* io = static_cast<asio::io_service*>(user_data);
		io->poll();
		return G_SOURCE_CONTINUE;
	}, &io_service);
	g_timeout_add(1000, timeout_callback, loop);
	g_main_loop_run(loop);
	g_main_loop_unref(loop);

	// serverThread.join();

	return 0;
}

Hey Jongho,

Unfortunately, it’s not possible for you to have a single bot join multiple meetings at the same time. Zoom’s SDK is generally designed to handle one meeting session per instance.

To solve this, you should create individual instances of your bot for each meeting. Think of each instance as its own separate bot, each dedicated to one meeting. Using this approach, you’ll be able to have bots join multiple meetings.

You also might want to check out the Recall.ai API.

It’s a simple 3rd party API that lets you use meeting bots to get raw audio/video/metadata from meetings without you needing to spend months to build, scale and maintain these bots.

Here are the API docs: https://docs.recall.ai

@jongho77 you can consider running each instances of the Linux Meeting SDK on docker