-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: added sip outgoing hook, UI and demo (#22)
- Loading branch information
Showing
37 changed files
with
4,184 additions
and
56 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
"use server"; | ||
|
||
import { env } from "../env"; | ||
import { generate_random_token } from "./token"; | ||
|
||
interface MakeCallParams { | ||
sip_server: string; | ||
sip_auth?: { | ||
username: string; | ||
password: string; | ||
}; | ||
from_number: string; | ||
to_number: string; | ||
hook: string; | ||
} | ||
|
||
interface MakeCallResponse { | ||
status: boolean, | ||
data?: { | ||
gateway: string, | ||
call_id: string, | ||
call_token: string, | ||
call_ws: string | ||
}, | ||
error?: string | ||
} | ||
|
||
export async function make_outgoing_call(params: MakeCallParams) { | ||
console.log("Creating webrtc token"); | ||
const [room, peer, token] = await generate_random_token(); | ||
const url = env.SIP_GATEWAY + "/call/outgoing"; | ||
const rawResponse = await fetch(url, { | ||
method: "POST", | ||
headers: { | ||
Authorization: "Bearer " + env.APP_SECRET, | ||
Accept: "application/json", | ||
"Content-Type": "application/json", | ||
}, | ||
body: JSON.stringify({ | ||
...params, | ||
streaming: { | ||
room, | ||
peer: params.to_number, | ||
record: false, | ||
}, | ||
}), | ||
cache: "no-cache", | ||
}); | ||
|
||
const content = await rawResponse.json() as MakeCallResponse; | ||
if (content.status && content.data) { | ||
return { | ||
room: room!, | ||
peer: peer!, | ||
token: token!, | ||
callTo: params.to_number, | ||
callWs: env.SIP_GATEWAY + content.data.call_ws, | ||
} | ||
} else { | ||
throw new Error(content.error); | ||
} | ||
} | ||
|
||
interface CreateNotifyTokenParams { | ||
client_id: string, | ||
ttl: number, | ||
} | ||
|
||
interface CreateNotifyTokenResponse { | ||
status: boolean, | ||
data?: { | ||
token: string | ||
}, | ||
error?: string | ||
} | ||
|
||
export async function create_notify_ws(params: CreateNotifyTokenParams) { | ||
console.log("Creating notify token"); | ||
const url = env.SIP_GATEWAY + "/token/notify"; | ||
const rawResponse = await fetch(url, { | ||
method: "POST", | ||
headers: { | ||
Authorization: "Bearer " + env.APP_SECRET, | ||
Accept: "application/json", | ||
"Content-Type": "application/json", | ||
}, | ||
body: JSON.stringify(params), | ||
cache: "no-cache", | ||
}); | ||
|
||
const content = await rawResponse.json() as CreateNotifyTokenResponse; | ||
if (content.status && content.data) { | ||
return env.SIP_GATEWAY + "/call/incoming/notify?token=" + content.data.token; | ||
} else { | ||
throw new Error(content.error); | ||
} | ||
} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
"use client"; | ||
|
||
import { Atm0sMediaProvider } from "@atm0s-media-sdk/react-hooks"; | ||
import { Atm0sMediaUIProvider, SipIncomingCallWidget } from "@atm0s-media-sdk/react-ui"; | ||
import { useCallback, useState } from "react"; | ||
import { env } from "../../env"; | ||
import { AudioMixerMode } from "@atm0s-media-sdk/core"; | ||
|
||
export interface IncomingCallPanelProps { | ||
callFrom: string, | ||
callWs: string, | ||
room: string; | ||
peer: string, | ||
token: string, | ||
record: boolean, | ||
onEnd?: () => void; | ||
} | ||
|
||
export default function PageContent({ callFrom, callWs, room, peer, token, record, onEnd }: IncomingCallPanelProps) { | ||
const [active, setActive] = useState(true); | ||
const onEnd2 = useCallback(() => { | ||
setActive(false); | ||
onEnd && onEnd(); | ||
}, [onEnd]) | ||
|
||
return ( | ||
<main className="flex flex-col items-center justify-center min-h-screen bg-gray-100"> | ||
<div className="mb-4"> | ||
{active && <Atm0sMediaProvider | ||
gateway={env.GATEWAY_ENDPOINTS[0]!} | ||
cfg={{ | ||
token, | ||
join: { | ||
room, | ||
peer, | ||
publish: { peer: true, tracks: true }, | ||
subscribe: { peers: true, tracks: true }, | ||
features: { | ||
mixer: { | ||
mode: AudioMixerMode.AUTO, | ||
outputs: 3 | ||
} | ||
} | ||
}, | ||
}} | ||
> | ||
<Atm0sMediaUIProvider> | ||
<SipIncomingCallWidget callFrom={callFrom} callWs={callWs} room={room} record={record} onEnd={onEnd2} /> | ||
</Atm0sMediaUIProvider> | ||
</Atm0sMediaProvider>} | ||
</div> | ||
</main> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
"use client"; | ||
|
||
import { generate_random_token } from "../../actions/token"; | ||
import Content, { IncomingCallPanelProps } from "./content"; | ||
import { useState } from "react"; | ||
|
||
export default function SipIncoming({ | ||
searchParams, | ||
}: { | ||
searchParams: { server?: string }; | ||
}) { | ||
// Create a single session state object using the Session interface | ||
const [session, setSession] = useState<IncomingCallPanelProps | null>(null); | ||
|
||
const [callWs, setCallWs] = useState(""); | ||
const [callFrom, setCallFrom] = useState(""); | ||
const [record, setRecord] = useState(false); | ||
|
||
const handleCall = async () => { | ||
const [room, peer, token] = await generate_random_token(); | ||
setSession({ | ||
callFrom, | ||
callWs, | ||
room, | ||
peer, | ||
token, | ||
record, | ||
}) | ||
}; | ||
|
||
return ( | ||
<div className="flex items-center justify-center min-h-screen bg-gray-100"> {/* Centering the form */} | ||
<form onSubmit={(e) => { e.preventDefault(); handleCall(); }} className="bg-white p-4 rounded-lg shadow-md space-y-4 w-96"> {/* Reduced padding and width */} | ||
<h2 className="text-2xl font-bold text-center text-gray-800 mb-4">Show a Incoming SIP Call</h2> {/* Reduced title size */} | ||
<input | ||
type="text" | ||
placeholder="Websocket Call URL" | ||
value={callWs} | ||
onChange={(e) => setCallWs(e.target.value)} | ||
className="input-class p-2 border border-gray-300 rounded-lg w-full focus:outline-none focus:ring-2 focus:ring-blue-500" // Reduced padding | ||
/> | ||
<input | ||
type="text" | ||
placeholder="Call From" | ||
value={callFrom} | ||
onChange={(e) => setCallFrom(e.target.value)} | ||
className="input-class p-2 border border-gray-300 rounded-lg w-full focus:outline-none focus:ring-2 focus:ring-blue-500" // Reduced padding | ||
/> | ||
|
||
<div className="flex items-center"> | ||
<input | ||
type="checkbox" | ||
checked={record} | ||
onChange={(e) => setRecord(e.target.checked)} | ||
className="mr-2" | ||
/> | ||
<label className="text-gray-700">Record Call</label> | ||
</div> | ||
|
||
<button type="submit" className="button-class w-full bg-blue-600 text-white p-2 rounded-lg hover:bg-blue-700 transition duration-200">Show</button> | ||
</form> | ||
{session && <Content {...session} onEnd={() => setSession(null)} />} {/* Pass session object to Content */} | ||
</div> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
"use client"; | ||
|
||
import { AudioMixerMode } from "@atm0s-media-sdk/core"; | ||
import { Atm0sMediaProvider } from "@atm0s-media-sdk/react-hooks"; | ||
import { Atm0sMediaUIProvider, SipOutgoingCallWidget } from "@atm0s-media-sdk/react-ui"; | ||
import { useCallback, useState } from "react"; | ||
import { env } from "../../env"; | ||
|
||
export interface OutgoingCallPanelProps { | ||
room: string; | ||
peer: string; | ||
token: string; | ||
callTo: string, | ||
callWs: string; | ||
onEnd?: () => void; | ||
} | ||
|
||
export default function PageContent({ room, peer, token, callTo, callWs, onEnd }: OutgoingCallPanelProps) { | ||
const [active, setActive] = useState(true); | ||
const hangUp = useCallback(() => { | ||
setActive(false); | ||
onEnd && onEnd(); | ||
}, [onEnd]) | ||
|
||
return ( | ||
<main> | ||
{active && <Atm0sMediaProvider | ||
gateway={env.GATEWAY_ENDPOINTS[0]!} | ||
cfg={{ | ||
token, | ||
join: { | ||
room, | ||
peer, | ||
publish: { peer: true, tracks: true }, | ||
subscribe: { peers: true, tracks: true }, | ||
features: { | ||
mixer: { | ||
mode: AudioMixerMode.AUTO, | ||
outputs: 3 | ||
} | ||
} | ||
}, | ||
}} | ||
> | ||
<Atm0sMediaUIProvider> | ||
<SipOutgoingCallWidget callTo={callTo} callWs={callWs} onEnd={hangUp} /> | ||
</Atm0sMediaUIProvider> | ||
</Atm0sMediaProvider>} | ||
</main> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
"use client"; | ||
|
||
import { generate_random_token } from "../../actions/token"; | ||
import { make_outgoing_call } from "../../actions/sip"; // Import the server action | ||
import Content, { OutgoingCallPanelProps } from "./content"; | ||
import { useState } from "react"; | ||
|
||
export default function SipOutgoing({ | ||
searchParams, | ||
}: { | ||
searchParams: { server?: string }; | ||
}) { | ||
// New state variables for the input form | ||
const [sipServer, setSipServer] = useState(""); | ||
const [sipUser, setSipUser] = useState(""); | ||
const [sipPassword, setSipPassword] = useState(""); | ||
const [sipFrom, setSipFrom] = useState(""); | ||
const [sipTo, setSipTo] = useState(""); | ||
const [sipHook, setSipHook] = useState(""); | ||
|
||
const [outgoingProps, setOutgoingProps] = useState<OutgoingCallPanelProps | null>(null) | ||
|
||
// Function to handle the call button click | ||
const handleCall = async () => { | ||
const props = await make_outgoing_call({ | ||
sip_server: sipServer, | ||
sip_auth: sipUser ? { | ||
username: sipUser, | ||
password: sipPassword, | ||
} : undefined, | ||
from_number: sipFrom, | ||
to_number: sipTo, | ||
hook: sipHook, | ||
}); | ||
// Logic to display the outgoing call with callWs and streamingToken | ||
setOutgoingProps(props) | ||
}; | ||
|
||
return ( | ||
<div className="flex items-center justify-center min-h-screen bg-gray-100"> {/* Centering the form */} | ||
<form onSubmit={(e) => { e.preventDefault(); handleCall(); }} className="bg-white p-4 rounded-lg shadow-md space-y-4 w-96"> {/* Reduced padding and width */} | ||
<h2 className="text-2xl font-bold text-center text-gray-800 mb-4">Make a SIP Call</h2> {/* Reduced title size */} | ||
<input | ||
type="text" | ||
placeholder="SIP Server" | ||
value={sipServer} | ||
onChange={(e) => setSipServer(e.target.value)} | ||
className="input-class p-2 border border-gray-300 rounded-lg w-full focus:outline-none focus:ring-2 focus:ring-blue-500" // Reduced padding | ||
/> | ||
<input | ||
type="text" | ||
placeholder="SIP User" | ||
value={sipUser} | ||
onChange={(e) => setSipUser(e.target.value)} | ||
className="input-class p-2 border border-gray-300 rounded-lg w-full focus:outline-none focus:ring-2 focus:ring-blue-500" // Reduced padding | ||
/> | ||
<input | ||
type="password" | ||
placeholder="SIP Password" | ||
value={sipPassword} | ||
onChange={(e) => setSipPassword(e.target.value)} | ||
className="input-class p-2 border border-gray-300 rounded-lg w-full focus:outline-none focus:ring-2 focus:ring-blue-500" // Reduced padding | ||
/> | ||
<input | ||
type="text" | ||
placeholder="SIP From" | ||
value={sipFrom} | ||
onChange={(e) => setSipFrom(e.target.value)} | ||
className="input-class p-2 border border-gray-300 rounded-lg w-full focus:outline-none focus:ring-2 focus:ring-blue-500" // Reduced padding | ||
/> | ||
<input | ||
type="text" | ||
placeholder="SIP To" | ||
value={sipTo} | ||
onChange={(e) => setSipTo(e.target.value)} | ||
className="input-class p-2 border border-gray-300 rounded-lg w-full focus:outline-none focus:ring-2 focus:ring-blue-500" // Reduced padding | ||
/> | ||
<input | ||
type="text" | ||
placeholder="SIP Hook" | ||
value={sipHook} | ||
onChange={(e) => setSipHook(e.target.value)} | ||
className="input-class p-2 border border-gray-300 rounded-lg w-full focus:outline-none focus:ring-2 focus:ring-blue-500" // Reduced padding | ||
/> | ||
<button type="submit" className="button-class w-full bg-blue-600 text-white p-2 rounded-lg hover:bg-blue-700 transition duration-200">Call</button> {/* Reduced button padding */} | ||
</form> | ||
|
||
{outgoingProps && <Content {...outgoingProps} onEnd={() => setOutgoingProps(null)} />} | ||
</div> | ||
); | ||
} |
Empty file.
Oops, something went wrong.