import { Injectable } from '@angular/core'; import { HttpService } from './http.service'; import * as Parse from 'parse'; import { AlertController, ToastController } from '@ionic/angular'; // import AgoraRTM from 'agora-rtmClient'; import { Subject } from 'rxjs'; import { Router } from '@angular/router'; declare const AgoraRTM: any; @Injectable({ providedIn: 'root', }) export class MessageService { private eventSource = new Subject(); event$ = this.eventSource.asObservable(); company: string = 'Qje9D4bqol'; rtmClient: any; // RTM实例 // rtmClientMap: any = {}; channelNameList: any = {}; //订阅频道状态 msChannelName: string = 'global_room'; // 全球频道 options: any = { connectState: false, }; appid?: string; userId: string = Parse.User.current()?.id!; pageFun?: Function; //页面传入的方法 alert: any; // 弹窗 constructor( private alertController: AlertController, private router: Router, public toastController: ToastController, private http: HttpService ) {} messageMapList: any = {}; /* 获取token */ async getToken() { this.userId = Parse.User.current()?.id!; //获取频道token记录 let uid = Parse.User.current()?.id; let baseurl = 'https://server.fmode.cn/api/webrtc/build_token'; let reqBody = { company: this.company, channelName: this.msChannelName, type: 'withrtm', account: uid, }; let data: any = await this.http.httpRequst(baseurl, reqBody, 'POST'); console.log(data); if (data.code == 200) { // this.options[channel] = { // token: data.data.token, // }; this.options.token = data.data.token; this.appid = data.data.appid; } } async initRTM() { // let states = ['CONNECTED', 'CONNECTING']; if (this.options.connectState) return; await this.getToken(); const rtmConfig = { logLevel: 'debug' }; this.rtmClient = new AgoraRTM.RTM(this.appid, this.userId,rtmConfig); this.joinReady(); await this.loginRTM(); // this.subscribeMessage(channelName); // this.publishMessage('user-online', channelName, 'USER'); //用户上线通知 } /**@监听频道消息 *@'USERCALLINVITATION': 用户通话邀请 *@'CLOASEINVITATION': 取消通话邀请 *@'REFUSEINVITATION_' + uid: 拒绝通话邀请 *@'RESPONSEINVITOIN_' + uid: 接受邀请 */ joinReady() { this.rtmClient?.addEventListener('message', async (event: any) => { console.log('接收到一条消息:', event); let states = [ 'USERCALLINVITATION', 'REFUSEINVITATION_' + this.userId, 'CLOASEINVITATION', 'RESPONSEINVITOIN_' + this.userId, ]; const message = JSON.parse(event.message); let is_self = event.publisher == this.userId; console.log('自己发出的消息:', is_self, message.text); if (states.includes(message.text)) { if (!is_self) { this.callPresence(message.text, event.publisher, event.channelName); } return; } this.showMessage(event); }); this.rtmClient?.addEventListener('presence', (event: any) => { console.log('频道人员状态变化 ', event); let is_self = event.publisher == this.userId; //远端用户离开频道,主播在线状态 if ( !is_self && event.eventType === 'REMOTE_LEAVE' && event.channelName === this.userId ) { this.setConnectState(this.userId, 'ONLINE'); } }); this.rtmClient?.addEventListener('linkState', (event: any) => { console.log('连接状态: ', event); }); } /* 设置频道状态 */ async setConnectState(channelName: string, mode: string) { const channelType = 'MESSAGE'; const states = { Mode: mode, }; try { let res = await this.rtmClient?.presence.setState(channelName, channelType, states); console.log('频道状态发生更改:',res); } catch (err: any) { console.log(err); } } /* 呼叫事件 */ async callPresence(message: string, publisher: string, channelName: string) { console.log(message); let userData = await this.getUserMetadata(publisher); let toast; switch (message) { case 'USERCALLINVITATION': await this.setConnectState(this.userId, 'CONNECTING'); console.log(`收到${userData.nickname.value ?? '未知用户'}通话邀请`); this.alert = await this.alertController.create({ cssClass: 'my-custom-class', header: '通话邀请', message: `收到${userData.nickname.value ?? '未知用户'}通话邀请`, backdropDismiss: false, buttons: [ { text: '拒绝', role: 'cancel', handler: async (blah) => { await this.setConnectState(this.userId, 'ONLINE'); this.publishMessage( 'REFUSEINVITATION_' + publisher, channelName ); }, }, { text: '接受', cssClass: 'secondary', handler: async () => { this.publishMessage( 'RESPONSEINVITOIN_' + publisher, channelName ); let rid = await this.getRoom(this.userId); this.router.navigate(['live/link-room/' + rid]); }, }, ], }); await this.alert.present(); break; case 'CLOASEINVITATION': await this.setConnectState(this.userId, 'ONLINE'); console.log(`${userData.nickname.value ?? '未知用户'}取消通话`); this.alert?.dismiss(); toast = await this.toastController.create({ message: '对方已取消通话邀请', color: 'warning', duration: 1500, }); toast.present(); break; case 'REFUSEINVITATION_' + this.userId: console.log(`${userData.nickname.value ?? '未知用户'}拒绝通话`); this.alert?.dismiss(); this.eventSource.next(false); break; case 'RESPONSEINVITOIN_' + this.userId: console.log( `${userData.nickname.value ?? '未知用户'}同意通话,进入视频通话` ); this.alert?.dismiss(); this.eventSource.next(true); break; } } async getRoom(uid: string): Promise { let query = new Parse.Query('Room'); query.equalTo('company', this.company); query.equalTo('user', uid); query.notEqualTo('isDeleted', true); query.select('objectId'); let r = await query.first(); return r?.id; } /* 加入频道 */ // async join(channelName: string) { // const options = { // token: this.options[channelName].token, // withPresence: true, // withLock: true, // withMetadata: true, // }; // try { // const streamChannel = // this.rtmClientMap[channelName].rtmClient.createStreamChannel( // channelName // ); // const result = await streamChannel.join(options); // } catch (status) { // console.error('join channel failed: ', status); // } // } async loginRTM() { try { await this.rtmClient?.login({ token: this.options.token, }); this.options.connectState = true; // 登录成功 let userMateData = await this.getUserMetadata(this.userId); if (!userMateData) { let user = Parse.User.current(); const metadata = [ { key: 'nickname', value: user?.get('nickname') || user?.get('name') || user?.id, }, { key: 'avatar', value: user?.get('avatar') ?? 'https://file-cloud.fmode.cn/DXNgcD6zo6/20221202/j6p8kb034039.png', }, ]; const options = { userId: this.userId, addTimeStamp: true, addUserId: true, }; let result = await this.rtmClient?.storage.setUserMetadata( metadata, options ); console.log(JSON.stringify(result)); } } catch (status) { console.log(status); } } logOutRTM() { this.rtmClient?.logout().then(() => { console.log('logout success'); this.options.connectState = false; }); } /* 订阅消息 */ subscribeMessage(channelName: string, param?: any, callback?: any) { if (this.channelNameList[channelName]) return; return new Promise((resolve, reject) => { const options = { withMessage: param?.message ?? false, // message 事件 withPresence: param?.presence ?? false, // presence 事件 beQuiet: false, // quiet 事件 withMetadata: false, // metadata 事件 withLock: false, // lock 事件 }; this.rtmClient .subscribe(channelName, options) .then((res: any) => { console.log('subscribeMessage', res); //订阅成功 this.channelNameList[channelName] = true; if (!this.messageMapList[channelName]) { this.messageMapList[channelName] = []; this.getHistoryMessage(channelName); } this.pageFun?.(); resolve(true); }) .catch((err: any) => { console.error('subscribeMessageErr', err); reject(false); }); }); } async getHistoryMessage(channelName: string) { let query = new Parse.Query('MessageLog'); query.equalTo('channel', channelName); query.descending('createdAt'); query.skip(this.messageMapList[channelName].length); query.limit(50); query.select('content', 'from_id'); let msgList = await query.find(); console.log('历史消息:', msgList); msgList.forEach((item: any) => { let is_self = item?.get('from_id') == this.userId; let data: any = item?.get('content'); data['is_self'] = is_self; data['istoday'] = true; data['timestamp'] = new Date(data.timestamp); this.messageMapList[channelName].unshift(data); }); // this.messageMapList[channelName].unshift(...msgList); } /* 取消订阅 */ unsubscribeMessage(channelName: string) { this.rtmClient .unsubscribe(channelName) .then((res: any) => { console.log('unsubscribeMessage', res); //订阅成功 this.channelNameList[channelName] = false; }) .catch((err: any) => { console.error('unsubscribeMessage', err); }); } async showMessage(param: any) { let userData = await this.getUserMetadata(param.publisher); let is_self = param.publisher == this.userId; // console.log(userData); let message = JSON.parse(param.message); if (!this.messageMapList[param.channelName]) this.messageMapList[param.channelName] = []; if (is_self) { this.saveMeesage({ from_id: this.userId, from_username: Parse.User.current()?.get('nickname') ?? '未知用户', target_id: param.channelName, // target_name: userData.nickname.value ?? '未知用户', channel: param.channelName, content: { avatar: userData.avatar.value ?? 'https://file-cloud.fmode.cn/DXNgcD6zo6/20221202/j6p8kb034039.png', msg_type: 1, name: userData.nickname.value ?? '未知用户', content: message?.text ?? '', publisher: param.publisher, timestamp: param.timestamp, }, }); } let data: any = { is_self: is_self, avatar: userData.avatar.value ?? 'https://file-cloud.fmode.cn/DXNgcD6zo6/20221202/j6p8kb034039.png', msg_type: 1, name: userData.nickname.value ?? '未知用户', content: message?.text ?? '', publisher: param.publisher, timestamp: new Date(param.timestamp), istoday: true, }; this.messageMapList[param.channelName].push(data); this.pageFun?.(); } async saveMeesage(parse: { from_id: string; from_username: string; target_id: string; // target_name: string; channel: string; content: Object; }) { console.log(parse); let obj = Parse.Object.extend('MessageLog'); let message = new obj(); message.set('from_id', parse.from_id); message.set('from_username', parse.from_username); message.set('target_id', parse.target_id); // message.set('target_name', parse.target_name); message.set('channel', parse.channel); message.set('content', parse.content); message.set('company', { __type: 'Pointer', className: 'Company', objectId: this.company, }); await message.save(); } async getUserMetadata(uid: string) { try { const result = await this.rtmClient?.storage.getUserMetadata({ userId: uid, }); return result.metadata; } catch (status) { console.log(JSON.stringify(status)); } } async publishMessage( message: string, channelName: string, channelType?: string ) { const payload = { type: 'text', text: message }; const publishMessage = JSON.stringify(payload); const publishOptions = { channelType: channelType ?? 'MESSAGE' }; try { const result = await this.rtmClient?.publish( channelName, publishMessage, publishOptions ); console.log(result); } catch (status) { console.log(status); } } }