Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: transcriber detect bots and leave #532

Merged
merged 8 commits into from
Apr 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions jigasi-home/sip-communicator.properties
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,9 @@ net.java.sip.communicator.impl.protocol.jabber.acc-xmpp-1.DOMAIN_BASE=<<DOMAIN_B
# we can receive dial/hangup only from the control muc
[email protected].<<DOMAIN_BASE>>

# when checking other participants whether they are jibri/jigasi we can also check the the domain they use for connecting
#org.jitsi.jigasi.TRUSTED_DOMAINS=["recorder.<<DOMAIN_BASE>>", "sipjibri.<<DOMAIN_BASE>>", "jigasi.<<DOMAIN_BASE>>"]

org.jitsi.jigasi.BREWERY_ENABLED=true

# We can use the prefix org.jitsi.jigasi.xmpp.acc to override any of the
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/org/jitsi/jigasi/AudioModeration.java
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ public AudioModeration(JvbConference jvbConference, SipGatewaySession gatewaySes
* @param meetTools the <tt>OperationSetJitsiMeetTools</tt> instance.
* @return Returns the features extension element that can be added to presence.
*/
static ExtensionElement addSupportedFeatures(OperationSetJitsiMeetToolsJabber meetTools)
static ExtensionElement getSupportedFeatures(OperationSetJitsiMeetToolsJabber meetTools)
{
if (isMutingSupported())
{
Expand Down
11 changes: 11 additions & 0 deletions src/main/java/org/jitsi/jigasi/JigasiBundleActivator.java
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,17 @@ public void startWithServices(final BundleContext bundleContext)

StartMutedProvider.registerStartMutedProvider();

ProviderManager.addExtensionProvider(
FeaturesExtension.ELEMENT,
FeaturesExtension.NAMESPACE,
new DefaultPacketExtensionProvider<>(FeaturesExtension.class)
);
ProviderManager.addExtensionProvider(
FeatureExtension.ELEMENT,
FeatureExtension.NAMESPACE,
new DefaultPacketExtensionProvider<>(FeatureExtension.class)
);

if (isSipEnabled())
{
if (isSipStartMutedEnabled())
Expand Down
120 changes: 66 additions & 54 deletions src/main/java/org/jitsi/jigasi/JvbConference.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,12 @@
import net.java.sip.communicator.service.protocol.event.*;
import net.java.sip.communicator.service.protocol.jabber.*;
import net.java.sip.communicator.service.protocol.media.*;
import net.java.sip.communicator.util.DataObject;
import net.java.sip.communicator.util.osgi.ServiceUtils;
import net.java.sip.communicator.util.*;
import net.java.sip.communicator.util.osgi.*;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.client.utils.*;
import org.jitsi.impl.neomedia.*;
import org.jitsi.jigasi.lobby.Lobby;
import org.jitsi.jigasi.lobby.*;
import org.jitsi.jigasi.stats.*;
import org.jitsi.jigasi.util.*;
import org.jitsi.jigasi.version.*;
Expand Down Expand Up @@ -66,6 +66,11 @@
import static net.java.sip.communicator.service.protocol.event.LocalUserChatRoomPresenceChangeEvent.*;
import static org.jivesoftware.smack.packet.StanzaError.Condition.*;

import static org.jitsi.jigasi.lobby.Lobby.*;
import static org.jitsi.jigasi.TranscriptionGatewaySession.*;
import static org.jitsi.jigasi.util.Util.*;


/**
* Class takes care of handling Jitsi Videobridge conference. Currently, it waits
* for the first XMPP provider service to be registered and uses it to join the
Expand All @@ -90,13 +95,6 @@ public class JvbConference
*/
private final static Logger logger = Logger.getLogger(JvbConference.class);

/**
* The name of XMPP feature which states for Jigasi SIP Gateway and can be
* used to recognize gateway client.
*/
public static final String SIP_GATEWAY_FEATURE_NAME
= "http://jitsi.org/protocol/jigasi";

/**
* The name of XMPP feature for Jingle/DTMF feature (XEP-0181).
*/
Expand Down Expand Up @@ -192,29 +190,37 @@ public class JvbConference
* <tt>OperationSetJitsiMeetTools</tt> instance.
* @return Returns the 'features' extension element that can be added to presence.
*/
private static ExtensionElement addSupportedFeatures(
private ExtensionElement addSupportedFeatures(
OperationSetJitsiMeetToolsJabber meetTools)
{
FeaturesExtension features = new FeaturesExtension();

meetTools.addSupportedFeature(SIP_GATEWAY_FEATURE_NAME);
features.addChildExtension(Util.createFeature(SIP_GATEWAY_FEATURE_NAME));
meetTools.addSupportedFeature(DTMF_FEATURE_NAME);
features.addChildExtension(Util.createFeature(DTMF_FEATURE_NAME));
meetTools.addSupportedFeature(JIGASI_FEATURE_NAME);
features.addChildExtension(Util.createFeature(JIGASI_FEATURE_NAME));

if (this.isTranscriber)
{
meetTools.addSupportedFeature(TRANSCRIBER_FEATURE_NAME);
features.addChildExtension(Util.createFeature(TRANSCRIBER_FEATURE_NAME));
}
else
{
// dtmf is used only when sip calling
meetTools.addSupportedFeature(DTMF_FEATURE_NAME);
features.addChildExtension(Util.createFeature(DTMF_FEATURE_NAME));
}

ConfigurationService cfg
= JigasiBundleActivator.getConfigurationService();
ConfigurationService cfg = JigasiBundleActivator.getConfigurationService();

// Remove ICE support from features list ?
if (cfg.getBoolean(SipGateway.P_NAME_DISABLE_ICE, false))
{
meetTools.removeSupportedFeature(
"urn:xmpp:jingle:transports:ice-udp:1");
meetTools.removeSupportedFeature("urn:xmpp:jingle:transports:ice-udp:1");

logger.info("ICE feature will not be advertised");
}

ExtensionElement audioMuteFeature = AudioModeration.addSupportedFeatures(meetTools);
ExtensionElement audioMuteFeature = AudioModeration.getSupportedFeatures(meetTools);
if (audioMuteFeature != null)
{
features.addChildExtension(audioMuteFeature);
Expand All @@ -229,6 +235,11 @@ private static ExtensionElement addSupportedFeatures(
*/
private final AbstractGatewaySession gatewaySession;

/**
* Whether Jigasi will join as transcriber.
*/
private final boolean isTranscriber;

/**
* Whether to auto stop when only jigasi are left in the room.
*/
Expand Down Expand Up @@ -372,11 +383,6 @@ private static ExtensionElement addSupportedFeatures(
*/
private final RoomMetadataListener roomMetadataListener = new RoomMetadataListener();

/**
* Up-to-date list of participants in the room that are jigasi.
*/
private final List<String> jigasiChatRoomMembers = Collections.synchronizedList(new ArrayList<>());

/**
* The features for the current xmpp provider we will use later adding to the room presence we send.
*/
Expand All @@ -391,17 +397,18 @@ private static ExtensionElement addSupportedFeatures(
public JvbConference(AbstractGatewaySession gatewaySession, CallContext ctx)
{
this.gatewaySession = gatewaySession;
this.isTranscriber = this.gatewaySession instanceof TranscriptionGatewaySession;
this.callContext = ctx;
this.allowOnlyJigasiInRoom = JigasiBundleActivator.getConfigurationService()
.getBoolean(P_NAME_ALLOW_ONLY_JIGASIS_IN_ROOM, true);

if (this.gatewaySession instanceof SipGatewaySession)
if (this.isTranscriber)
{
this.audioModeration = new AudioModeration(this, (SipGatewaySession)this.gatewaySession, this.callContext);
this.audioModeration = null;
}
else
{
this.audioModeration = null;
this.audioModeration = new AudioModeration(this, (SipGatewaySession)this.gatewaySession, this.callContext);
}
}

Expand Down Expand Up @@ -772,7 +779,7 @@ private void discoverComponentAddresses()
.findFirst().orElse(null);

// we process room metadata messages only when we are transcribing
if (roomMetadataIdentity != null && this.gatewaySession instanceof TranscriptionGatewaySession)
if (roomMetadataIdentity != null && this.isTranscriber)
{
getConnection().addAsyncStanzaListener(roomMetadataListener,
new AndFilter(
Expand Down Expand Up @@ -874,9 +881,7 @@ public void joinConferenceRoom()
// creates an extension to hold all headers, as when using
// addPresencePacketExtensions it requires unique extensions
// otherwise overrides them
AbstractPacketExtension initiator
= new AbstractPacketExtension(
SIP_GATEWAY_FEATURE_NAME, "initiator"){};
AbstractPacketExtension initiator = new AbstractPacketExtension(JIGASI_FEATURE_NAME, "initiator"){};

// let's add all extra headers from the context
callContext.getExtraHeaders().forEach(
Expand Down Expand Up @@ -1206,18 +1211,8 @@ else if (ChatRoomMemberPresenceChangeEvent.MEMBER_UPDATED
{
if (member instanceof ChatRoomMemberJabberImpl)
{
Presence presence = ((ChatRoomMemberJabberImpl) member).getLastPresence();

gatewaySession.notifyChatRoomMemberUpdated(member, presence);

// let's check and whether it is a jigasi participant
// we use initiator as its easier for checking/parsing
if (presence != null
&& !jigasiChatRoomMembers.contains(member.getName())
&& presence.hasExtension("initiator", SIP_GATEWAY_FEATURE_NAME))
{
jigasiChatRoomMembers.add(member.getName());
}
gatewaySession.notifyChatRoomMemberUpdated(member,
((ChatRoomMemberJabberImpl) member).getLastPresence());
}
}

Expand All @@ -1230,8 +1225,6 @@ else if (ChatRoomMemberPresenceChangeEvent.MEMBER_UPDATED
this.callContext + " Member left : " + member.getRole()
+ " " + member.getContactAddress());

jigasiChatRoomMembers.remove(member.getName());

CallPeer peer;
if (jvbCall != null && (peer = jvbCall.getCallPeers().next()) instanceof MediaAwareCallPeer)
{
Expand Down Expand Up @@ -1301,7 +1294,8 @@ private void processChatRoomMemberLeft(ChatRoomMember member)
boolean onlyJigasisInRoom = this.mucRoom.getMembers().stream().allMatch(m ->
m.getName().equals(getResourceIdentifier().toString()) // ignore if it is us
|| m.getName().equals(gatewaySession.getFocusResourceAddr()) // ignore if it is jicofo
|| jigasiChatRoomMembers.contains(m.getName()));
|| (Util.isJigasi((ChatRoomMemberJabberImpl) m)
&& !Util.isTranscriberJigasi((ChatRoomMemberJabberImpl)m)));

if (onlyJigasisInRoom)
{
Expand All @@ -1327,6 +1321,25 @@ private void processChatRoomMemberLeft(ChatRoomMember member)
// transcriber case
stop();
}

return;
}
}

if (JvbConference.this.isTranscriber)
{
// make sure we hangup transcriber if only backend services are in the room - Jibri/Jigasi
// (maybe a second transcriber if there is some glitch in the system).
boolean onlyBotsInRoom = this.mucRoom.getMembers().stream().allMatch(m ->
m.getName().equals(getResourceIdentifier().toString()) // ignore if it is us
|| m.getName().equals(gatewaySession.getFocusResourceAddr()) // ignore if it is jicofo
|| Util.isTranscriberJigasi((ChatRoomMemberJabberImpl)m)
|| Util.isJibri((ChatRoomMemberJabberImpl)m));

if (onlyBotsInRoom)
{
logger.info(this.callContext + " Leaving room only bots in the room!");
stop();
}
}
}
Expand Down Expand Up @@ -2012,13 +2025,12 @@ private void updateFromRoomConfiguration()
discoverInfo(((ChatRoomJabberImpl)this.mucRoom).getIdentifierAsJid());

DataForm df = (DataForm) info.getExtension(DataForm.NAMESPACE);
boolean lobbyEnabled = df.getField(Lobby.DATA_FORM_LOBBY_ROOM_FIELD) != null;
boolean singleModeratorEnabled = df.getField(Lobby.DATA_FORM_SINGLE_MODERATOR_FIELD) != null;
boolean lobbyEnabled = df.getField(DATA_FORM_LOBBY_ROOM_FIELD) != null;
boolean singleModeratorEnabled = df.getField(DATA_FORM_SINGLE_MODERATOR_FIELD) != null;
setLobbyEnabled(lobbyEnabled);
this.singleModeratorEnabled = singleModeratorEnabled;

List<String> roomMetadataValues
= df.getField(TranscriptionGatewaySession.DATA_FORM_ROOM_METADATA_FIELD).getValuesAsString();
List<String> roomMetadataValues = df.getField(DATA_FORM_ROOM_METADATA_FIELD).getValuesAsString();
if (roomMetadataValues != null && !roomMetadataValues.isEmpty())
{
// it is supposed to have a single value
Expand All @@ -2033,7 +2045,7 @@ private void updateFromRoomConfiguration()

private void processRoomMetadataJson(String json)
{
if (!(this.gatewaySession instanceof TranscriptionGatewaySession))
if (!this.isTranscriber)
{
return;
}
Expand Down Expand Up @@ -2267,9 +2279,9 @@ private class MediaActivityChecker
public void run()
{
// if the call was stopped before we check ignore
if (!started)
if (!started || jvbCall == null)
{
logger.warn("Media activity checker exiting early as call is not started!");
logger.warn("Media activity checker exiting early as call is not started or jvbCall is stopped!");
return;
}

Expand Down
5 changes: 2 additions & 3 deletions src/main/java/org/jitsi/jigasi/TranscriptionGateway.java
Original file line number Diff line number Diff line change
Expand Up @@ -165,15 +165,14 @@ private String getTranscriberFromRemote(String remoteTsConfigUrl)
@Override
public TranscriptionGatewaySession createOutgoingCall(CallContext ctx)
{
String customTranscriptionServiceClass
= getCustomTranscriptionServiceClass(ctx.getTenant());
String customTranscriptionServiceClass = getCustomTranscriptionServiceClass(ctx.getTenant());
AbstractTranscriptionService service = null;
if (customTranscriptionServiceClass != null)
{
try
{
service = (AbstractTranscriptionService)Class.forName(
customTranscriptionServiceClass).getDeclaredConstructor().newInstance();
customTranscriptionServiceClass).getDeclaredConstructor().newInstance();
}
catch(Exception e)
{
Expand Down
Loading
Loading