Use USERNAMELOOKUP message to get the current SessionState for a model

This commit is contained in:
0xb00bface 2021-12-23 17:48:44 +01:00
parent cea5eac9de
commit 7577379885
2 changed files with 65 additions and 97 deletions

View File

@ -1,52 +1,32 @@
package ctbrec.sites.mfc;
import static ctbrec.io.HttpConstants.*;
import static ctbrec.sites.mfc.MessageTypes.*;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Base64;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
import java.util.Optional;
import java.util.Queue;
import java.util.Random;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.Consumer;
import org.json.JSONArray;
import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.squareup.moshi.JsonAdapter;
import com.squareup.moshi.Moshi;
import ctbrec.Config;
import ctbrec.StringUtil;
import ctbrec.io.HttpException;
import okhttp3.Cookie;
import okhttp3.Request;
import okhttp3.Response;
import okhttp3.WebSocket;
import okhttp3.WebSocketListener;
import okhttp3.*;
import okio.ByteString;
import org.json.JSONArray;
import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.URLDecoder;
import java.util.*;
import java.util.Map.Entry;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.Consumer;
import static ctbrec.io.HttpConstants.*;
import static ctbrec.sites.mfc.MessageTypes.*;
import static java.nio.charset.StandardCharsets.UTF_8;
public class MyFreeCamsClient {
@ -213,7 +193,7 @@ public class MyFreeCamsClient {
MyFreeCamsClient.this.ws = null;
}
private StringBuilder msgBuffer = new StringBuilder();
private final StringBuilder msgBuffer = new StringBuilder();
@Override
public void onMessage(WebSocket webSocket, String text) {
@ -284,7 +264,7 @@ public class MyFreeCamsClient {
break;
case EXTDATA:
if (message.getArg1() == MessageTypes.LOGIN) {
// noop
} else if (message.getArg1() == MessageTypes.MANAGELIST) {
requestExtData(message.getMessage());
} else {
@ -305,7 +285,7 @@ public class MyFreeCamsClient {
json = new JSONObject(message.getMessage());
tkx = json.getString("tkx");
cxid = json.getInt("cxid");
ctxenc = URLDecoder.decode(json.getString("ctxenc"), "utf-8");
ctxenc = URLDecoder.decode(json.getString("ctxenc"), UTF_8);
JSONArray ctxArray = json.getJSONArray("ctx");
ctx = new int[ctxArray.length()];
for (int i = 0; i < ctxArray.length(); i++) {
@ -317,8 +297,6 @@ public class MyFreeCamsClient {
break;
}
}
} catch (UnsupportedEncodingException e) {
LOG.error("Error while decoding ctxenc URL", e);
} catch (Exception e) {
LOG.error("Exception occured while processing websocket message {}", msgBuffer, e);
ws.close(1000, "");
@ -360,7 +338,7 @@ public class MyFreeCamsClient {
LOG.trace("Requesting EXTDATA {}", url);
try (Response resp = mfc.getHttpClient().execute(req)) {
if (resp.isSuccessful()) {
parseExtDataSessionStates(resp.body().string());
parseExtDataSessionStates(Objects.requireNonNull(resp.body(), "HTTP response is null").string());
}
}
} catch (Exception e) {
@ -483,7 +461,7 @@ public class MyFreeCamsClient {
return websocket;
}
private Message parseMessage(StringBuilder msgBuffer) throws UnsupportedEncodingException {
private Message parseMessage(StringBuilder msgBuffer) {
int packetLengthBytes = 6;
if (msgBuffer.length() < packetLengthBytes) {
// packet size not transmitted completely
@ -503,12 +481,12 @@ public class MyFreeCamsClient {
int receiver = parseNextInt(rawMessage);
int arg1 = parseNextInt(rawMessage);
int arg2 = parseNextInt(rawMessage);
Message message = new Message(type, sender, receiver, arg1, arg2, URLDecoder.decode(rawMessage.toString(), "utf-8"));
Message message = new Message(type, sender, receiver, arg1, arg2, URLDecoder.decode(rawMessage.toString(), UTF_8));
msgBuffer.delete(0, packetLength);
return message;
}
} catch (Exception e) {
LOG.error("StringBuilder contains invalid data {}", msgBuffer.toString(), e);
LOG.error("StringBuilder contains invalid data {}", msgBuffer, e);
String logfile = "mfc_messages.log";
try (FileOutputStream fout = new FileOutputStream(logfile)) {
for (String string : receivedTextHistory) {
@ -564,7 +542,7 @@ public class MyFreeCamsClient {
if (isBroadcasterOnOBS(state)) {
JSONArray array = new JSONArray();
Arrays.stream(ctx).forEach(array::put);
userData.put("vidctx", Base64.getEncoder().encodeToString(array.toString().getBytes(StandardCharsets.UTF_8)));
userData.put("vidctx", Base64.getEncoder().encodeToString(array.toString().getBytes(UTF_8)));
userData.put("cxid", cxid);
}
userData.put("mode", "DOWNLOAD");
@ -599,11 +577,10 @@ public class MyFreeCamsClient {
MyFreeCamsClient.this.ws = null;
}
} else {
// we are establishing a new connection at the moment, not need to
// do anything
// we are establishing a new connection at the moment, no need to do anything
}
Thread.sleep(TimeUnit.SECONDS.toMillis(15));
TimeUnit.SECONDS.sleep(15);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
LOG.warn("Websocket watchdog has been interrupted");
@ -621,7 +598,7 @@ public class MyFreeCamsClient {
for (SessionState state : sessionStates.asMap().values()) {
Optional<String> nm = Optional.ofNullable(state.getNm());
Optional<String> name = Optional.ofNullable(model.getName());
if(!nm.isPresent() || !name.isPresent()) {
if(nm.isEmpty() || name.isEmpty()) {
continue;
}
@ -675,8 +652,7 @@ public class MyFreeCamsClient {
}
public SessionState getSessionState(ctbrec.Model model) {
if (model instanceof MyFreeCamsModel) {
MyFreeCamsModel mfcModel = (MyFreeCamsModel) model;
if (model instanceof MyFreeCamsModel mfcModel) {
for (SessionState state : sessionStates.asMap().values()) {
if (mfcModel.getUid() > 0 && state.getUid() != null && state.getUid() > 0 && mfcModel.getUid() == state.getUid()) {
return state;
@ -691,39 +667,40 @@ public class MyFreeCamsClient {
}
public List<ctbrec.Model> search(String q) throws InterruptedException {
LOG.debug("Sending USERNAMELOOKUP for {}", q);
int msgId = messageId++;
Object monitor = new Object();
List<ctbrec.Model> result = new ArrayList<>();
responseHandlers.put(msgId, msg -> {
LOG.debug("Search result: {}", msg);
if (StringUtil.isNotBlank(msg.getMessage()) && !Objects.equals(msg.getMessage(), q)) {
JSONObject json = new JSONObject(msg.getMessage());
String name = json.getString("nm");
MyFreeCamsModel model = mfc.createModel(name);
model.setUid(json.getInt("uid"));
model.setMfcState(State.of(json.getInt("vs")));
String uid = Integer.toString(model.getUid());
String uidStart = uid.substring(0, 3);
String previewUrl = "https://img.mfcimg.com/photos2/" + uidStart + '/' + uid + "/avatar.90x90.jpg";
model.setPreview(previewUrl);
result.add(model);
}
if (ws != null) {
LOG.trace("Sending USERNAMELOOKUP for {}", q);
Object monitor = new Object();
int msgId = messageId++;
responseHandlers.put(msgId, msg -> {
LOG.trace("Search result: {}", msg);
if (StringUtil.isNotBlank(msg.getMessage()) && !Objects.equals(msg.getMessage(), q)) {
JSONObject json = new JSONObject(msg.getMessage());
String name = json.getString("nm");
MyFreeCamsModel model = mfc.createModel(name);
model.setUid(json.getInt("uid"));
model.setMfcState(State.of(json.getInt("vs")));
String uid = Integer.toString(model.getUid());
String uidStart = uid.substring(0, 3);
String previewUrl = "https://img.mfcimg.com/photos2/" + uidStart + '/' + uid + "/avatar.90x90.jpg";
model.setPreview(previewUrl);
result.add(model);
}
synchronized (monitor) {
monitor.notifyAll();
}
});
ws.send("10 " + sessionId + " 0 " + msgId + " 0 " + q + "\n");
synchronized (monitor) {
monitor.notifyAll();
monitor.wait();
}
});
ws.send("10 " + sessionId + " 0 " + msgId + " 0 " + q + "\n");
synchronized (monitor) {
monitor.wait();
}
for (MyFreeCamsModel model : models.asMap().values()) {
if (StringUtil.isNotBlank(model.getName()) && model.getName().toLowerCase().contains(q.toLowerCase())) {
result.add(model);
for (MyFreeCamsModel model : models.asMap().values()) {
if (StringUtil.isNotBlank(model.getName()) && model.getName().toLowerCase().contains(q.toLowerCase())) {
result.add(model);
}
}
}
return result;
}

View File

@ -59,22 +59,13 @@ public class MyFreeCamsModel extends AbstractModel {
@Override
public boolean isOnline(boolean ignoreCache) throws IOException, ExecutionException, InterruptedException {
if (ignoreCache) {
String sessionId = MyFreeCamsClient.getInstance().getSessionId(getName());
boolean online = !(sessionId.isEmpty() || sessionId.equals("0"));
SessionState sessionState = MyFreeCamsClient.getInstance().getSessionState(this);
if (online) {
if (sessionState == null) {
LOG.warn("MFC model {} [{}] seems to be online but a SessionState could not be found", getName(), getUid());
} else {
return state == ctbrec.sites.mfc.State.ONLINE;
}
} else {
state = ctbrec.sites.mfc.State.OFFLINE;
List<ctbrec.Model> searchResult = MyFreeCamsClient.getInstance().search(getName());
if (!searchResult.isEmpty()) {
MyFreeCamsModel m = (MyFreeCamsModel) searchResult.get(0);
this.onlineState = m.getOnlineState(true);
}
return online;
} else {
return isOnline();
}
return isOnline();
}
@Override
@ -369,7 +360,7 @@ public class MyFreeCamsModel extends AbstractModel {
Map<String, String> headers = new HashMap<>();
headers.put(ACCEPT, "*/*");
headers.put(ACCEPT_LANGUAGE, Locale.ENGLISH.getLanguage());
headers.put(CONNECTION, KEEP_ALIVE);
headers.put(CONNECTION, "close");
if (getSite() != null) {
headers.put(ORIGIN, getSite().getBaseUrl());
headers.put(REFERER, getSite().getBaseUrl());