diff --git a/.env.example b/.env.example index 0d57008..632908f 100644 --- a/.env.example +++ b/.env.example @@ -7,6 +7,7 @@ RTP_PUBLIC_IP=192.168.0.100 # DISCORD_OUTBOUND_SIP_HOST=192.168.0.25 # DISCORD_OUTBOUND_SIP_PORT=5060 # DISCORD_OUTBOUND_SIP_TRANSPORT=udp +# DISCORD_OUTBOUND_EXTENSION_PREFIX=*80 # DATA_DIR=/var/lib/sipcord # CONFIG_PATH=./config.toml # SOUNDS_DIR=./wav diff --git a/README.md b/README.md index 2ee06f4..c44c68b 100644 --- a/README.md +++ b/README.md @@ -73,6 +73,7 @@ RTP_PUBLIC_IP=192.168.0.100 DISCORD_OUTBOUND_SIP_HOST=192.168.0.25 DISCORD_OUTBOUND_SIP_PORT=5060 DISCORD_OUTBOUND_SIP_TRANSPORT=udp +DISCORD_OUTBOUND_EXTENSION_PREFIX=*80 ``` Set both IPs to the address other SIP devices use to reach the bridge. For @@ -85,6 +86,11 @@ Set `DISCORD_OUTBOUND_SIP_HOST` to the PBX or SIP server that should receive Discord-originated extension calls. For a FreePBX box at `192.168.0.25`, that means `DISCORD_OUTBOUND_SIP_HOST=192.168.0.25`. +By default, Discord-originated calls prepend `*80` to the requested extension so +FreePBX intercom/auto-answer targets work out of the box. Set +`DISCORD_OUTBOUND_EXTENSION_PREFIX=` to an empty string if you want plain +extension dialing instead. + Create a `docker-compose.yml`: ```yaml @@ -205,8 +211,11 @@ Usage: Behavior: - The user running `/call` must already be in a Discord voice channel. - The bot uses that voice channel as the bridge destination. -- The bridge dials the requested extension through the configured PBX target, for - example `sip:1101@192.168.0.25:5060;transport=udp`. +- The bridge dials the requested extension through the configured PBX target. +- By default it prepends `*80`, so `/call extension:1101` becomes something like + `sip:*801101@192.168.0.25:5060;transport=udp`. +- Set `DISCORD_OUTBOUND_EXTENSION_PREFIX=` to empty if you want `/call + extension:1101` to dial `1101` directly instead. - When the SIP side answers, the phone call is connected to the Discord voice channel where the command was run. @@ -252,6 +261,7 @@ Dial `1000` (or whatever you put in `dialplan.toml`) and you should hear the bot | `DISCORD_OUTBOUND_SIP_HOST` | *(disabled if unset)* | PBX/SIP host used by Discord `/call` | | `DISCORD_OUTBOUND_SIP_PORT` | `5060` | Port for Discord-originated outbound SIP calls | | `DISCORD_OUTBOUND_SIP_TRANSPORT` | `udp` | Transport for Discord-originated outbound SIP calls: `udp`, `tcp`, or `tls` | +| `DISCORD_OUTBOUND_EXTENSION_PREFIX` | `*80` | Prefix prepended before the requested extension for Discord-originated calls; set empty for direct dialing | | `CONFIG_PATH` | `./config.toml` | Path to config.toml | | `DIALPLAN_PATH` | `./dialplan.toml` | Path to dialplan.toml | | `SOUNDS_DIR` | `./wav` | Path to sound files directory | diff --git a/sipcord-bridge/src/config.rs b/sipcord-bridge/src/config.rs index d178593..7a6cdf0 100644 --- a/sipcord-bridge/src/config.rs +++ b/sipcord-bridge/src/config.rs @@ -71,6 +71,9 @@ fn default_discord_outbound_sip_port() -> u16 { fn default_discord_outbound_sip_transport() -> String { "udp".to_string() } +fn default_discord_outbound_extension_prefix() -> String { + "*80".to_string() +} /// All environment variables consumed by the bridge, deserialized once at startup. #[derive(Debug, Clone, serde::Deserialize)] @@ -117,6 +120,8 @@ pub struct EnvConfig { pub discord_outbound_sip_port: u16, #[serde(default = "default_discord_outbound_sip_transport")] pub discord_outbound_sip_transport: String, + #[serde(default = "default_discord_outbound_extension_prefix")] + pub discord_outbound_extension_prefix: String, } #[derive(Debug, Clone, Copy, PartialEq, Eq)] @@ -142,18 +147,28 @@ pub struct DiscordOutboundSipConfig { pub host: String, pub port: u16, pub transport: OutboundSipTransport, + pub extension_prefix: String, } impl DiscordOutboundSipConfig { pub fn build_sip_uri(&self, extension: &str) -> String { + let dialed_extension = format!("{}{}", self.extension_prefix, extension); match self.transport { OutboundSipTransport::Udp => { - format!("sip:{}@{}:{};transport=udp", extension, self.host, self.port) + format!( + "sip:{}@{}:{};transport=udp", + dialed_extension, self.host, self.port + ) } OutboundSipTransport::Tcp => { - format!("sip:{}@{}:{};transport=tcp", extension, self.host, self.port) + format!( + "sip:{}@{}:{};transport=tcp", + dialed_extension, self.host, self.port + ) + } + OutboundSipTransport::Tls => { + format!("sips:{}@{}:{}", dialed_extension, self.host, self.port) } - OutboundSipTransport::Tls => format!("sips:{}@{}:{}", extension, self.host, self.port), } } } @@ -230,6 +245,7 @@ impl EnvConfig { host, port: self.discord_outbound_sip_port, transport, + extension_prefix: self.discord_outbound_extension_prefix.clone(), }) } @@ -530,6 +546,7 @@ mod tests { discord_outbound_sip_host: None, discord_outbound_sip_port: 5060, discord_outbound_sip_transport: "udp".to_string(), + discord_outbound_extension_prefix: "*80".to_string(), }; assert_eq!(env.resolved_data_dir(), "."); } @@ -557,6 +574,7 @@ mod tests { discord_outbound_sip_host: None, discord_outbound_sip_port: 5060, discord_outbound_sip_transport: "udp".to_string(), + discord_outbound_extension_prefix: "*80".to_string(), }; assert_eq!(env.resolved_data_dir(), "/tmp"); } @@ -584,6 +602,7 @@ mod tests { discord_outbound_sip_host: None, discord_outbound_sip_port: 5060, discord_outbound_sip_transport: "udp".to_string(), + discord_outbound_extension_prefix: "*80".to_string(), }; let tls = env.to_tls_config(); assert_eq!(tls.cert_dir, PathBuf::from("/data/certs")); @@ -627,12 +646,13 @@ mod tests { discord_outbound_sip_host: Some("192.168.0.25".to_string()), discord_outbound_sip_port: 5060, discord_outbound_sip_transport: "udp".to_string(), + discord_outbound_extension_prefix: "*80".to_string(), }; let outbound = env.discord_outbound_sip_config().unwrap(); assert_eq!( outbound.build_sip_uri("1101"), - "sip:1101@192.168.0.25:5060;transport=udp" + "sip:*801101@192.168.0.25:5060;transport=udp" ); }