@sonoransoftware/sonoran.js 1.0.36 → 1.0.37

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,24 @@
1
+ PolyForm Noncommercial License 1.0.0
2
+
3
+ Copyright (c) [2025] [Sonoran Software Systems, LLC]
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the “Software”), to
7
+ use, copy, modify, and distribute the Software, for any noncommercial purpose,
8
+ subject to the following conditions:
9
+
10
+ 1. Noncommercial Use Only. You may not use the Software for a commercial
11
+ purpose. “Commercial purpose” means the sale, lease, license, or other
12
+ commercial exploitation of the Software, or the use of the Software to
13
+ provide a service or product for which you receive compensation.
14
+
15
+ 2. Compliance and Attribution. You must ensure that anyone who uses the
16
+ Software complies with this license, and you must retain the above
17
+ copyright notice and this license in all copies or substantial portions
18
+ of the Software.
19
+
20
+ 3. No Warranty. THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY
21
+ KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
22
+ OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
23
+
24
+ See https://polyformproject.org/licenses/noncommercial/1.0.0/ for the full license text.
@@ -442,6 +442,15 @@ export interface RadioSpeakerLocation {
442
442
  label: string;
443
443
  id: string;
444
444
  }
445
+ export type RadioTonePlayTargetType = 'channel' | 'group' | 'game';
446
+ export interface RadioTonePlayTarget {
447
+ label: string;
448
+ type: RadioTonePlayTargetType;
449
+ value: unknown;
450
+ group: number | null;
451
+ icon?: string;
452
+ color?: string;
453
+ }
445
454
  export interface RadioSetUserChannelsOptions {
446
455
  transmit?: number;
447
456
  scan?: number[];
@@ -504,3 +513,8 @@ export interface RadioSetInGameSpeakerLocationsPromiseResult {
504
513
  reason?: string;
505
514
  result?: string;
506
515
  }
516
+ export interface RadioPlayTonePromiseResult {
517
+ success: boolean;
518
+ reason?: string;
519
+ result?: string;
520
+ }
@@ -334,6 +334,13 @@ class REST extends events_1.EventEmitter {
334
334
  token: args[1]
335
335
  };
336
336
  }
337
+ case 'PLAY_TONE': {
338
+ return {
339
+ roomId: args[0],
340
+ tones: args[1],
341
+ playTo: args[2]
342
+ };
343
+ }
337
344
  default: {
338
345
  return args;
339
346
  }
@@ -378,6 +378,35 @@ class RequestManager extends events_1.EventEmitter {
378
378
  path = apiType.path;
379
379
  break;
380
380
  }
381
+ case 'PLAY_TONE': {
382
+ const auth = ensureAuth();
383
+ const roomIdRaw = payload === null || payload === void 0 ? void 0 : payload.roomId;
384
+ if (roomIdRaw === undefined || roomIdRaw === null) {
385
+ throw new Error('roomId is required for PLAY_TONE requests.');
386
+ }
387
+ const roomIdNumber = typeof roomIdRaw === 'number' ? roomIdRaw : Number(roomIdRaw);
388
+ if (Number.isNaN(roomIdNumber)) {
389
+ throw new Error('roomId must be a number for PLAY_TONE requests.');
390
+ }
391
+ const tones = payload === null || payload === void 0 ? void 0 : payload.tones;
392
+ if (!Array.isArray(tones) || tones.length === 0) {
393
+ throw new Error('tones array is required for PLAY_TONE requests.');
394
+ }
395
+ const playTo = payload === null || payload === void 0 ? void 0 : payload.playTo;
396
+ if (!Array.isArray(playTo) || playTo.length === 0) {
397
+ throw new Error('playTo array is required for PLAY_TONE requests.');
398
+ }
399
+ method = 'POST';
400
+ body = {
401
+ id: auth.id,
402
+ key: auth.key,
403
+ roomId: roomIdNumber,
404
+ tones,
405
+ playTo
406
+ };
407
+ path = apiType.path;
408
+ break;
409
+ }
381
410
  default: {
382
411
  throw new Error(`Unsupported radio API type received: ${apiType.type}`);
383
412
  }
@@ -1,4 +1,4 @@
1
- import { productEnums, RadioSetUserChannelsOptions, RadioSpeakerLocation, CMSProfileFieldUpdate, CMSSetGameServerStruct, CMSTriggerPromotionFlowPayload } from '../../../../../constants';
1
+ import { productEnums, RadioSetUserChannelsOptions, RadioSpeakerLocation, RadioTonePlayTarget, CMSProfileFieldUpdate, CMSSetGameServerStruct, CMSTriggerPromotionFlowPayload } from '../../../../../constants';
2
2
  import type { RESTOptions } from '../REST';
3
3
  export declare const DefaultUserAgent = "Sonoran.js NPM Module";
4
4
  export declare const DefaultCADRestOptions: Required<RESTOptions>;
@@ -38,7 +38,7 @@ export declare const CommunitiesCMSAPITypes: APITypeData[];
38
38
  export declare const ERLCMSAPITypes: APITypeData[];
39
39
  export declare const RadioAPITypes: APITypeData[];
40
40
  export declare const AllAPITypes: AllAPITypeData[];
41
- export type AllAPITypesType = 'GET_SERVERS' | 'SET_SERVERS' | 'GET_VERSION' | 'SET_PENAL_CODES' | 'SET_API_ID' | 'GET_TEMPLATES' | 'NEW_RECORD' | 'EDIT_RECORD' | 'REMOVE_RECORD' | 'LOOKUP_INT' | 'LOOKUP' | 'GET_ACCOUNT' | 'CHECK_APIID' | 'APPLY_PERMISSION_KEY' | 'SET_ACCOUNT_PERMISSIONS' | 'BAN_USER' | 'VERIFY_SECRET' | 'AUTH_STREETSIGNS' | 'SET_POSTALS' | 'SEND_PHOTO' | 'SET_CLOCK' | 'JOIN_COMMUNITY' | 'LEAVE_COMMUNITY' | 'GET_CHARACTERS' | 'NEW_CHARACTER' | 'EDIT_CHARACTER' | 'REMOVE_CHARACTER' | 'GET_IDENTIFIERS' | 'MODIFY_IDENTIFIER' | 'SET_IDENTIFIER' | 'UNIT_PANIC' | 'UNIT_STATUS' | 'GET_BLIPS' | 'ADD_BLIP' | 'MODIFY_BLIP' | 'REMOVE_BLIP' | '911_CALL' | 'REMOVE_911' | 'GET_CALLS' | 'GET_ACTIVE_UNITS' | 'KICK_UNIT' | 'NEW_DISPATCH' | 'ATTACH_UNIT' | 'DETACH_UNIT' | 'SET_CALL_POSTAL' | 'SET_CALL_PRIMARY' | 'ADD_CALL_NOTE' | 'CLOSE_CALL' | 'UNIT_LOCATION' | 'SET_STREETSIGN_CONFIG' | 'UPDATE_STREETSIGN' | 'GET_COM_ACCOUNT' | 'GET_DEPARTMENTS' | 'GET_SUB_VERSION' | 'GET_CURRENT_CLOCK_IN' | 'GET_ACCOUNTS' | 'GET_PROFILE_FIELDS' | 'CHECK_COM_APIID' | 'VERIFY_WHITELIST' | 'CLOCK_IN_OUT' | 'FULL_WHITELIST' | 'GET_ACCOUNT_RANKS' | 'SET_ACCOUNT_RANKS' | 'RSVP' | 'CHANGE_FORM_STAGE' | 'GET_FORM_TEMPLATE_SUBMISSIONS' | 'KICK_ACCOUNT' | 'BAN_ACCOUNT' | 'LOOKUP' | 'EDIT_ACC_PROFLIE_FIELDS' | 'SET_ACCOUNT_NAME' | 'FORCE_SYNC' | 'TRIGGER_PROMOTION_FLOWS' | 'GET_PROMOTION_FLOWS' | 'SET_GAME_SERVERS' | 'ERLC_GET_ONLINE_PLAYERS' | 'ERLC_GET_PLAYER_QUEUE' | 'ERLC_ADD_NEW_RECORD' | 'RADIO_GET_COMMUNITY_CHANNELS' | 'RADIO_GET_CONNECTED_USERS' | 'RADIO_GET_CONNECTED_USER' | 'RADIO_SET_USER_CHANNELS' | 'RADIO_SET_USER_DISPLAY_NAME' | 'RADIO_GET_SERVER_SUBSCRIPTION_FROM_IP' | 'RADIO_SET_SERVER_IP' | 'RADIO_SET_IN_GAME_SPEAKER_LOCATIONS';
41
+ export type AllAPITypesType = 'GET_SERVERS' | 'SET_SERVERS' | 'GET_VERSION' | 'SET_PENAL_CODES' | 'SET_API_ID' | 'GET_TEMPLATES' | 'NEW_RECORD' | 'EDIT_RECORD' | 'REMOVE_RECORD' | 'LOOKUP_INT' | 'LOOKUP' | 'GET_ACCOUNT' | 'CHECK_APIID' | 'APPLY_PERMISSION_KEY' | 'SET_ACCOUNT_PERMISSIONS' | 'BAN_USER' | 'VERIFY_SECRET' | 'AUTH_STREETSIGNS' | 'SET_POSTALS' | 'SEND_PHOTO' | 'SET_CLOCK' | 'JOIN_COMMUNITY' | 'LEAVE_COMMUNITY' | 'GET_CHARACTERS' | 'NEW_CHARACTER' | 'EDIT_CHARACTER' | 'REMOVE_CHARACTER' | 'GET_IDENTIFIERS' | 'MODIFY_IDENTIFIER' | 'SET_IDENTIFIER' | 'UNIT_PANIC' | 'UNIT_STATUS' | 'GET_BLIPS' | 'ADD_BLIP' | 'MODIFY_BLIP' | 'REMOVE_BLIP' | '911_CALL' | 'REMOVE_911' | 'GET_CALLS' | 'GET_ACTIVE_UNITS' | 'KICK_UNIT' | 'NEW_DISPATCH' | 'ATTACH_UNIT' | 'DETACH_UNIT' | 'SET_CALL_POSTAL' | 'SET_CALL_PRIMARY' | 'ADD_CALL_NOTE' | 'CLOSE_CALL' | 'UNIT_LOCATION' | 'SET_STREETSIGN_CONFIG' | 'UPDATE_STREETSIGN' | 'GET_COM_ACCOUNT' | 'GET_DEPARTMENTS' | 'GET_SUB_VERSION' | 'GET_CURRENT_CLOCK_IN' | 'GET_ACCOUNTS' | 'GET_PROFILE_FIELDS' | 'CHECK_COM_APIID' | 'VERIFY_WHITELIST' | 'CLOCK_IN_OUT' | 'FULL_WHITELIST' | 'GET_ACCOUNT_RANKS' | 'SET_ACCOUNT_RANKS' | 'RSVP' | 'CHANGE_FORM_STAGE' | 'GET_FORM_TEMPLATE_SUBMISSIONS' | 'KICK_ACCOUNT' | 'BAN_ACCOUNT' | 'LOOKUP' | 'EDIT_ACC_PROFLIE_FIELDS' | 'SET_ACCOUNT_NAME' | 'FORCE_SYNC' | 'TRIGGER_PROMOTION_FLOWS' | 'GET_PROMOTION_FLOWS' | 'SET_GAME_SERVERS' | 'ERLC_GET_ONLINE_PLAYERS' | 'ERLC_GET_PLAYER_QUEUE' | 'ERLC_ADD_NEW_RECORD' | 'RADIO_GET_COMMUNITY_CHANNELS' | 'RADIO_GET_CONNECTED_USERS' | 'RADIO_GET_CONNECTED_USER' | 'RADIO_SET_USER_CHANNELS' | 'RADIO_SET_USER_DISPLAY_NAME' | 'RADIO_GET_SERVER_SUBSCRIPTION_FROM_IP' | 'RADIO_SET_SERVER_IP' | 'RADIO_SET_IN_GAME_SPEAKER_LOCATIONS' | 'PLAY_TONE';
42
42
  export interface CMSServerAPIStruct {
43
43
  id: number;
44
44
  name: string;
@@ -577,6 +577,11 @@ export interface RESTTypedAPIDataStructs {
577
577
  locations: RadioSpeakerLocation[],
578
578
  token?: string
579
579
  ];
580
+ PLAY_TONE: [
581
+ roomId: number,
582
+ tones: number[],
583
+ playTo: RadioTonePlayTarget[]
584
+ ];
580
585
  }
581
586
  export type PossibleRequestData = undefined | {
582
587
  data: CADPenalCodeStruct[] | CADSetAPIIDStruct | CADNewEditRecordOptionOneStruct | CADNewEditRecordOptionTwoStruct | CADLookupByIntStruct | CADLookupStruct | CADModifyAccountPermsStruct | CADKickBanUserStruct | CADSetPostalStruct[] | CADModifyIdentifierStruct | CADAddBlipStruct[] | CADModifyBlipStruct[] | CADGetCallsStruct | CADGetActiveUnitsStruct | CADNewDispatchStruct | CMSTriggerPromotionFlowPayload[];
@@ -561,6 +561,12 @@ exports.RadioAPITypes = [
561
561
  path: 'radio/set-server-speakers',
562
562
  method: 'POST',
563
563
  minVersion: 0
564
+ },
565
+ {
566
+ type: 'PLAY_TONE',
567
+ path: 'api/play-tone',
568
+ method: 'POST',
569
+ minVersion: 0
564
570
  }
565
571
  ];
566
572
  function formatForAll(array, product) {
@@ -52,4 +52,11 @@ export declare class RadioManager extends BaseManager {
52
52
  * @param {string} [token] Optional bearer token for authorization. Defaults to the community API key.
53
53
  */
54
54
  setInGameSpeakerLocations(locations: globalTypes.RadioSpeakerLocation[], token?: string): Promise<globalTypes.RadioSetInGameSpeakerLocationsPromiseResult>;
55
+ /**
56
+ * Plays one or more tones to radio channels, groups, or in-game speakers.
57
+ * @param {number} roomId Multi-server room id for tone playback.
58
+ * @param {number[]} tones Tone identifiers to play.
59
+ * @param {globalTypes.RadioTonePlayTarget[]} playTo Targets that should receive the tones.
60
+ */
61
+ playTone(roomId: number, tones: number[], playTo: globalTypes.RadioTonePlayTarget[]): Promise<globalTypes.RadioPlayTonePromiseResult>;
55
62
  }
@@ -220,5 +220,28 @@ class RadioManager extends BaseManager_1.BaseManager {
220
220
  }
221
221
  });
222
222
  }
223
+ /**
224
+ * Plays one or more tones to radio channels, groups, or in-game speakers.
225
+ * @param {number} roomId Multi-server room id for tone playback.
226
+ * @param {number[]} tones Tone identifiers to play.
227
+ * @param {globalTypes.RadioTonePlayTarget[]} playTo Targets that should receive the tones.
228
+ */
229
+ async playTone(roomId, tones, playTo) {
230
+ return new Promise(async (resolve, reject) => {
231
+ var _a, _b;
232
+ try {
233
+ const response = await ((_a = this.rest) === null || _a === void 0 ? void 0 : _a.request('PLAY_TONE', roomId, tones, playTo));
234
+ resolve({ success: true, result: (_b = response === null || response === void 0 ? void 0 : response.result) !== null && _b !== void 0 ? _b : response });
235
+ }
236
+ catch (err) {
237
+ if (err instanceof src_1.APIError) {
238
+ resolve({ success: false, reason: err.response });
239
+ }
240
+ else {
241
+ reject(err);
242
+ }
243
+ }
244
+ });
245
+ }
223
246
  }
224
247
  exports.RadioManager = RadioManager;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sonoransoftware/sonoran.js",
3
- "version": "1.0.36",
3
+ "version": "1.0.37",
4
4
  "description": "Sonoran.js is a library that allows you to interact with the Sonoran CAD and Sonoran CMS API. Based off of and utilizes several Discord.js library techniques for ease of use.",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
package/readme.md CHANGED
@@ -1,5 +1,10 @@
1
1
  # Sonoran.js
2
- Sonoran.js is a library that allows you to interact with the [Sonoran CAD](https://sonorancad.com/) and [Sonoran CMS](https://sonorancms.com/) API. Based off of and utilizes several Discord.js library techniques for ease of use.
2
+ Sonoran.js is a library that allows you to interact with the [Sonoran CAD](https://docs.sonoransoftware.com/cad), [Sonoran Radio](https://docs.sonoransoftware.com/radio), and [Sonoran CMS](https://docs.sonoransoftware.com/cms) API. Based off of and utilizes several Discord.js library techniques for ease of use.
3
+
4
+ ## Installation
5
+ ```js
6
+ npm i @sonoransoftware/sonoran.js
7
+ ```
3
8
 
4
9
  ## Example Instance Setup
5
10
 
@@ -458,5 +463,14 @@ await instance.radio.setInGameSpeakerLocations(
458
463
  );
459
464
  ```
460
465
 
466
+ ### playTone(roomId, tones, playTo)
467
+ Dispatches tones to channels, groups, or in-game speakers.
468
+ ```js
469
+ await instance.radio.playTone(1, [1001, 1002], [
470
+ { label: 'Primary Dispatch', type: 'channel', value: 10, group: 2 },
471
+ { label: 'Station Speakers', type: 'game', value: 'station-1', group: null }
472
+ ]);
473
+ ```
474
+
461
475
  ## Further Documentation
462
476
  More documentation for Sonoran CAD specific methods and usage can be found [here](/docs/CAD-Methods-and-Usage.md), Sonoran CMS specific methods and usage can be found [here](/docs/CMS-Methods-and-Usage.md), and usage information for the REST class [here](/docs/REST-Methods-and-Usage.md).
package/src/constants.ts CHANGED
@@ -498,6 +498,17 @@ export interface RadioSpeakerLocation {
498
498
  id: string;
499
499
  }
500
500
 
501
+ export type RadioTonePlayTargetType = 'channel' | 'group' | 'game';
502
+
503
+ export interface RadioTonePlayTarget {
504
+ label: string;
505
+ type: RadioTonePlayTargetType;
506
+ value: unknown;
507
+ group: number | null;
508
+ icon?: string;
509
+ color?: string;
510
+ }
511
+
501
512
  export interface RadioSetUserChannelsOptions {
502
513
  transmit?: number;
503
514
  scan?: number[];
@@ -569,3 +580,9 @@ export interface RadioSetInGameSpeakerLocationsPromiseResult {
569
580
  reason?: string;
570
581
  result?: string;
571
582
  }
583
+
584
+ export interface RadioPlayTonePromiseResult {
585
+ success: boolean;
586
+ reason?: string;
587
+ result?: string;
588
+ }
@@ -438,6 +438,13 @@ export class REST extends EventEmitter {
438
438
  token: args[1]
439
439
  }
440
440
  }
441
+ case 'PLAY_TONE': {
442
+ return {
443
+ roomId: args[0],
444
+ tones: args[1],
445
+ playTo: args[2]
446
+ }
447
+ }
441
448
  default: {
442
449
  return args;
443
450
  }
@@ -457,6 +457,35 @@ export class RequestManager extends EventEmitter {
457
457
  path = apiType.path;
458
458
  break;
459
459
  }
460
+ case 'PLAY_TONE': {
461
+ const auth = ensureAuth();
462
+ const roomIdRaw = payload?.roomId;
463
+ if (roomIdRaw === undefined || roomIdRaw === null) {
464
+ throw new Error('roomId is required for PLAY_TONE requests.');
465
+ }
466
+ const roomIdNumber = typeof roomIdRaw === 'number' ? roomIdRaw : Number(roomIdRaw);
467
+ if (Number.isNaN(roomIdNumber)) {
468
+ throw new Error('roomId must be a number for PLAY_TONE requests.');
469
+ }
470
+ const tones = payload?.tones;
471
+ if (!Array.isArray(tones) || tones.length === 0) {
472
+ throw new Error('tones array is required for PLAY_TONE requests.');
473
+ }
474
+ const playTo = payload?.playTo;
475
+ if (!Array.isArray(playTo) || playTo.length === 0) {
476
+ throw new Error('playTo array is required for PLAY_TONE requests.');
477
+ }
478
+ method = 'POST';
479
+ body = {
480
+ id: auth.id,
481
+ key: auth.key,
482
+ roomId: roomIdNumber,
483
+ tones,
484
+ playTo
485
+ };
486
+ path = apiType.path;
487
+ break;
488
+ }
460
489
  default: {
461
490
  throw new Error(`Unsupported radio API type received: ${apiType.type}`);
462
491
  }
@@ -1,4 +1,4 @@
1
- import { productEnums, RadioSetUserChannelsOptions, RadioSpeakerLocation, CMSProfileFieldUpdate, CMSSetGameServerStruct, CMSTriggerPromotionFlowPayload } from '../../../../../constants';
1
+ import { productEnums, RadioSetUserChannelsOptions, RadioSpeakerLocation, RadioTonePlayTarget, CMSProfileFieldUpdate, CMSSetGameServerStruct, CMSTriggerPromotionFlowPayload } from '../../../../../constants';
2
2
  import type { RESTOptions } from '../REST';
3
3
 
4
4
  export const DefaultUserAgent = 'Sonoran.js NPM Module';
@@ -599,6 +599,12 @@ export const RadioAPITypes: APITypeData[] = [
599
599
  path: 'radio/set-server-speakers',
600
600
  method: 'POST',
601
601
  minVersion: 0
602
+ },
603
+ {
604
+ type: 'PLAY_TONE',
605
+ path: 'api/play-tone',
606
+ method: 'POST',
607
+ minVersion: 0
602
608
  }
603
609
  ];
604
610
 
@@ -613,7 +619,7 @@ function formatForAll(array: APITypeData[], product: productEnums): AllAPITypeDa
613
619
 
614
620
  export const AllAPITypes: AllAPITypeData[] = [ ...formatForAll(GeneralCADAPITypes, productEnums.CAD), ...formatForAll(CivilianCADAPITypes, productEnums.CAD), ...formatForAll(EmergencyCADAPITypes, productEnums.CAD), ...formatForAll(GeneralCMSAPITypes, productEnums.CMS), ...formatForAll(ServersCMSAPITypes, productEnums.CMS), ...formatForAll(EventsCMSAPITypes, productEnums.CMS), ...formatForAll(FormsCMSAPITypes, productEnums.CMS), ...formatForAll(CommunitiesCMSAPITypes, productEnums.CMS), ...formatForAll(ERLCMSAPITypes, productEnums.CMS), ...formatForAll(RadioAPITypes, productEnums.RADIO) ];
615
621
 
616
- export type AllAPITypesType = 'GET_SERVERS' | 'SET_SERVERS' | 'GET_VERSION' | 'SET_PENAL_CODES' | 'SET_API_ID' | 'GET_TEMPLATES' | 'NEW_RECORD' | 'EDIT_RECORD' | 'REMOVE_RECORD' | 'LOOKUP_INT' | 'LOOKUP' | 'GET_ACCOUNT' | 'CHECK_APIID' | 'APPLY_PERMISSION_KEY' | 'SET_ACCOUNT_PERMISSIONS' | 'BAN_USER' | 'VERIFY_SECRET' | 'AUTH_STREETSIGNS' | 'SET_POSTALS' | 'SEND_PHOTO' | 'SET_CLOCK' | 'JOIN_COMMUNITY' | 'LEAVE_COMMUNITY' | 'GET_CHARACTERS' | 'NEW_CHARACTER' | 'EDIT_CHARACTER' | 'REMOVE_CHARACTER' | 'GET_IDENTIFIERS' | 'MODIFY_IDENTIFIER' | 'SET_IDENTIFIER' | 'UNIT_PANIC' | 'UNIT_STATUS' | 'GET_BLIPS' | 'ADD_BLIP' | 'MODIFY_BLIP' | 'REMOVE_BLIP' | '911_CALL' | 'REMOVE_911' | 'GET_CALLS' | 'GET_ACTIVE_UNITS' | 'KICK_UNIT' | 'NEW_DISPATCH' | 'ATTACH_UNIT' | 'DETACH_UNIT' | 'SET_CALL_POSTAL' | 'SET_CALL_PRIMARY' | 'ADD_CALL_NOTE' | 'CLOSE_CALL' | 'UNIT_LOCATION' | 'SET_STREETSIGN_CONFIG' | 'UPDATE_STREETSIGN' | 'GET_COM_ACCOUNT' | 'GET_DEPARTMENTS' | 'GET_SUB_VERSION' | 'GET_CURRENT_CLOCK_IN' | 'GET_ACCOUNTS' | 'GET_PROFILE_FIELDS' | 'CHECK_COM_APIID' | 'VERIFY_WHITELIST' | 'CLOCK_IN_OUT' | 'FULL_WHITELIST' | 'GET_ACCOUNT_RANKS' | 'SET_ACCOUNT_RANKS' | 'RSVP' | 'CHANGE_FORM_STAGE' | 'GET_FORM_TEMPLATE_SUBMISSIONS' | 'KICK_ACCOUNT' | 'BAN_ACCOUNT' | 'LOOKUP' | 'EDIT_ACC_PROFLIE_FIELDS' | 'SET_ACCOUNT_NAME' | 'FORCE_SYNC' | 'TRIGGER_PROMOTION_FLOWS' | 'GET_PROMOTION_FLOWS' | 'SET_GAME_SERVERS' | 'ERLC_GET_ONLINE_PLAYERS' | 'ERLC_GET_PLAYER_QUEUE' | 'ERLC_ADD_NEW_RECORD' | 'RADIO_GET_COMMUNITY_CHANNELS' | 'RADIO_GET_CONNECTED_USERS' | 'RADIO_GET_CONNECTED_USER' | 'RADIO_SET_USER_CHANNELS' | 'RADIO_SET_USER_DISPLAY_NAME' | 'RADIO_GET_SERVER_SUBSCRIPTION_FROM_IP' | 'RADIO_SET_SERVER_IP' | 'RADIO_SET_IN_GAME_SPEAKER_LOCATIONS';
622
+ export type AllAPITypesType = 'GET_SERVERS' | 'SET_SERVERS' | 'GET_VERSION' | 'SET_PENAL_CODES' | 'SET_API_ID' | 'GET_TEMPLATES' | 'NEW_RECORD' | 'EDIT_RECORD' | 'REMOVE_RECORD' | 'LOOKUP_INT' | 'LOOKUP' | 'GET_ACCOUNT' | 'CHECK_APIID' | 'APPLY_PERMISSION_KEY' | 'SET_ACCOUNT_PERMISSIONS' | 'BAN_USER' | 'VERIFY_SECRET' | 'AUTH_STREETSIGNS' | 'SET_POSTALS' | 'SEND_PHOTO' | 'SET_CLOCK' | 'JOIN_COMMUNITY' | 'LEAVE_COMMUNITY' | 'GET_CHARACTERS' | 'NEW_CHARACTER' | 'EDIT_CHARACTER' | 'REMOVE_CHARACTER' | 'GET_IDENTIFIERS' | 'MODIFY_IDENTIFIER' | 'SET_IDENTIFIER' | 'UNIT_PANIC' | 'UNIT_STATUS' | 'GET_BLIPS' | 'ADD_BLIP' | 'MODIFY_BLIP' | 'REMOVE_BLIP' | '911_CALL' | 'REMOVE_911' | 'GET_CALLS' | 'GET_ACTIVE_UNITS' | 'KICK_UNIT' | 'NEW_DISPATCH' | 'ATTACH_UNIT' | 'DETACH_UNIT' | 'SET_CALL_POSTAL' | 'SET_CALL_PRIMARY' | 'ADD_CALL_NOTE' | 'CLOSE_CALL' | 'UNIT_LOCATION' | 'SET_STREETSIGN_CONFIG' | 'UPDATE_STREETSIGN' | 'GET_COM_ACCOUNT' | 'GET_DEPARTMENTS' | 'GET_SUB_VERSION' | 'GET_CURRENT_CLOCK_IN' | 'GET_ACCOUNTS' | 'GET_PROFILE_FIELDS' | 'CHECK_COM_APIID' | 'VERIFY_WHITELIST' | 'CLOCK_IN_OUT' | 'FULL_WHITELIST' | 'GET_ACCOUNT_RANKS' | 'SET_ACCOUNT_RANKS' | 'RSVP' | 'CHANGE_FORM_STAGE' | 'GET_FORM_TEMPLATE_SUBMISSIONS' | 'KICK_ACCOUNT' | 'BAN_ACCOUNT' | 'LOOKUP' | 'EDIT_ACC_PROFLIE_FIELDS' | 'SET_ACCOUNT_NAME' | 'FORCE_SYNC' | 'TRIGGER_PROMOTION_FLOWS' | 'GET_PROMOTION_FLOWS' | 'SET_GAME_SERVERS' | 'ERLC_GET_ONLINE_PLAYERS' | 'ERLC_GET_PLAYER_QUEUE' | 'ERLC_ADD_NEW_RECORD' | 'RADIO_GET_COMMUNITY_CHANNELS' | 'RADIO_GET_CONNECTED_USERS' | 'RADIO_GET_CONNECTED_USER' | 'RADIO_SET_USER_CHANNELS' | 'RADIO_SET_USER_DISPLAY_NAME' | 'RADIO_GET_SERVER_SUBSCRIPTION_FROM_IP' | 'RADIO_SET_SERVER_IP' | 'RADIO_SET_IN_GAME_SPEAKER_LOCATIONS' | 'PLAY_TONE';
617
623
 
618
624
  export interface CMSServerAPIStruct {
619
625
  id: number;
@@ -1187,6 +1193,11 @@ export interface RESTTypedAPIDataStructs {
1187
1193
  locations: RadioSpeakerLocation[],
1188
1194
  token?: string
1189
1195
  ];
1196
+ PLAY_TONE: [
1197
+ roomId: number,
1198
+ tones: number[],
1199
+ playTo: RadioTonePlayTarget[]
1200
+ ];
1190
1201
  }
1191
1202
 
1192
1203
  export type PossibleRequestData =
@@ -184,4 +184,25 @@ export class RadioManager extends BaseManager {
184
184
  }
185
185
  });
186
186
  }
187
+
188
+ /**
189
+ * Plays one or more tones to radio channels, groups, or in-game speakers.
190
+ * @param {number} roomId Multi-server room id for tone playback.
191
+ * @param {number[]} tones Tone identifiers to play.
192
+ * @param {globalTypes.RadioTonePlayTarget[]} playTo Targets that should receive the tones.
193
+ */
194
+ public async playTone(roomId: number, tones: number[], playTo: globalTypes.RadioTonePlayTarget[]): Promise<globalTypes.RadioPlayTonePromiseResult> {
195
+ return new Promise(async (resolve, reject) => {
196
+ try {
197
+ const response: any = await this.rest?.request('PLAY_TONE', roomId, tones, playTo);
198
+ resolve({ success: true, result: response?.result ?? response });
199
+ } catch (err) {
200
+ if (err instanceof APIError) {
201
+ resolve({ success: false, reason: err.response });
202
+ } else {
203
+ reject(err);
204
+ }
205
+ }
206
+ });
207
+ }
187
208
  }