Merge branch 'GameLogSQLite'

This commit is contained in:
Natsumi
2021-09-26 22:32:34 +13:00
12 changed files with 2594 additions and 1752 deletions
+29 -13
View File
@@ -229,20 +229,15 @@ namespace VRCX
return CpuMonitor.Instance.CpuUsage;
}
public void CacheImage(string Base64File)
{
String Icon = Path.Combine(Program.AppDataDirectory, "cache\\toast");
File.WriteAllBytes(Icon, Convert.FromBase64String(Base64File));
}
public void DesktopNotification(string BoldText, string Text, bool Image)
public void DesktopNotification(string BoldText, string Text, string Image)
{
XmlDocument toastXml = ToastNotificationManager.GetTemplateContent(ToastTemplateType.ToastImageAndText02);
XmlNodeList stringElements = toastXml.GetElementsByTagName("text");
String imagePath = Path.Combine(Program.BaseDirectory, "VRCX.ico");
if (Image)
if (!String.IsNullOrEmpty(Image))
{
imagePath = Path.Combine(Program.AppDataDirectory, "cache\\toast");
File.WriteAllBytes(imagePath, Convert.FromBase64String(Image));
}
stringElements[0].AppendChild(toastXml.CreateTextNode(BoldText));
stringElements[1].AppendChild(toastXml.CreateTextNode(Text));
@@ -268,14 +263,20 @@ namespace VRCX
public string sourceApp { get; set; }
}
public void XSNotification(string Title, string Content, int Timeout, bool Image)
public void XSNotification(string Title, string Content, int Timeout, string Image)
{
bool UseBase64Icon = true;
String Icon = "iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAACXBIWXMAAAsTAAALEwEAmpwYAAAHaGlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNS42LWMxNDUgNzkuMTYzNDk5LCAyMDE4LzA4LzEzLTE2OjQwOjIyICAgICAgICAiPiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPiA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIgeG1sbnM6cGhvdG9zaG9wPSJodHRwOi8vbnMuYWRvYmUuY29tL3Bob3Rvc2hvcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RFdnQ9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZUV2ZW50IyIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ0MgMjAxOSAoV2luZG93cykiIHhtcDpDcmVhdGVEYXRlPSIyMDIxLTA0LTA4VDE0OjU3OjAxKzEyOjAwIiB4bXA6TW9kaWZ5RGF0ZT0iMjAyMS0wNC0wOFQxNjozMzoxMCsxMjowMCIgeG1wOk1ldGFkYXRhRGF0ZT0iMjAyMS0wNC0wOFQxNjozMzoxMCsxMjowMCIgZGM6Zm9ybWF0PSJpbWFnZS9wbmciIHBob3Rvc2hvcDpDb2xvck1vZGU9IjMiIHBob3Rvc2hvcDpJQ0NQcm9maWxlPSJzUkdCIElFQzYxOTY2LTIuMSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDo2YTY5MmQzYi03ZTJkLTNiNGUtYTMzZC1hN2MwOTNlOGU0OTkiIHhtcE1NOkRvY3VtZW50SUQ9ImFkb2JlOmRvY2lkOnBob3Rvc2hvcDo1NTE2MWIyMi1hYzgxLTY3NDYtODAyYi1kODIzYWFmN2RjYjciIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDo3ZjJjNTA2ZS02YTVhLWRhNGEtOTg5Mi02NDZiMzQ0MGQxZTgiPiA8cGhvdG9zaG9wOkRvY3VtZW50QW5jZXN0b3JzPiA8cmRmOkJhZz4gPHJkZjpsaT5hZG9iZTpkb2NpZDpwaG90b3Nob3A6NmJmOGE5MTgtY2QzZS03OTRjLTk3NzktMzM0YjYwZWJiNTYyPC9yZGY6bGk+IDwvcmRmOkJhZz4gPC9waG90b3Nob3A6RG9jdW1lbnRBbmNlc3RvcnM+IDx4bXBNTTpIaXN0b3J5PiA8cmRmOlNlcT4gPHJkZjpsaSBzdEV2dDphY3Rpb249ImNyZWF0ZWQiIHN0RXZ0Omluc3RhbmNlSUQ9InhtcC5paWQ6N2YyYzUwNmUtNmE1YS1kYTRhLTk4OTItNjQ2YjM0NDBkMWU4IiBzdEV2dDp3aGVuPSIyMDIxLTA0LTA4VDE0OjU3OjAxKzEyOjAwIiBzdEV2dDpzb2Z0d2FyZUFnZW50PSJBZG9iZSBQaG90b3Nob3AgQ0MgMjAxOSAoV2luZG93cykiLz4gPHJkZjpsaSBzdEV2dDphY3Rpb249InNhdmVkIiBzdEV2dDppbnN0YW5jZUlEPSJ4bXAuaWlkOmJhM2ZjODI3LTM0ZjQtYjU0OC05ZGFiLTZhMTZlZmQzZjAxMSIgc3RFdnQ6d2hlbj0iMjAyMS0wNC0wOFQxNTowMTozMSsxMjowMCIgc3RFdnQ6c29mdHdhcmVBZ2VudD0iQWRvYmUgUGhvdG9zaG9wIENDIDIwMTkgKFdpbmRvd3MpIiBzdEV2dDpjaGFuZ2VkPSIvIi8+IDxyZGY6bGkgc3RFdnQ6YWN0aW9uPSJzYXZlZCIgc3RFdnQ6aW5zdGFuY2VJRD0ieG1wLmlpZDo2YTY5MmQzYi03ZTJkLTNiNGUtYTMzZC1hN2MwOTNlOGU0OTkiIHN0RXZ0OndoZW49IjIwMjEtMDQtMDhUMTY6MzM6MTArMTI6MDAiIHN0RXZ0OnNv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";
if (Image)
bool UseBase64Icon;
string Icon;
if (String.IsNullOrEmpty(Image))
{
UseBase64Icon = true;
Icon = "iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAACXBIWXMAAAsTAAALEwEAmpwYAAAHaGlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNS42LWMxNDUgNzkuMTYzNDk5LCAyMDE4LzA4LzEzLTE2OjQwOjIyICAgICAgICAiPiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPiA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIgeG1sbnM6cGhvdG9zaG9wPSJodHRwOi8vbnMuYWRvYmUuY29tL3Bob3Rvc2hvcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RFdnQ9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZUV2ZW50IyIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ0MgMjAxOSAoV2luZG93cykiIHhtcDpDcmVhdGVEYXRlPSIyMDIxLTA0LTA4VDE0OjU3OjAxKzEyOjAwIiB4bXA6TW9kaWZ5RGF0ZT0iMjAyMS0wNC0wOFQxNjozMzoxMCsxMjowMCIgeG1wOk1ldGFkYXRhRGF0ZT0iMjAyMS0wNC0wOFQxNjozMzoxMCsxMjowMCIgZGM6Zm9ybWF0PSJpbWFnZS9wbmciIHBob3Rvc2hvcDpDb2xvck1vZGU9IjMiIHBob3Rvc2hvcDpJQ0NQcm9maWxlPSJzUkdCIElFQzYxOTY2LTIuMSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDo2YTY5MmQzYi03ZTJkLTNiNGUtYTMzZC1hN2MwOTNlOGU0OTkiIHhtcE1NOkRvY3VtZW50SUQ9ImFkb2JlOmRvY2lkOnBob3Rvc2hvcDo1NTE2MWIyMi1hYzgxLTY3NDYtODAyYi1kODIzYWFmN2RjYjciIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDo3ZjJjNTA2ZS02YTVhLWRhNGEtOTg5Mi02NDZiMzQ0MGQxZTgiPiA8cGhvdG9zaG9wOkRvY3VtZW50QW5jZXN0b3JzPiA8cmRmOkJhZz4gPHJkZjpsaT5hZG9iZTpkb2NpZDpwaG90b3Nob3A6NmJmOGE5MTgtY2QzZS03OTRjLTk3NzktMzM0YjYwZWJiNTYyPC9yZGY6bGk+IDwvcmRmOkJhZz4gPC9waG90b3Nob3A6RG9jdW1lbnRBbmNlc3RvcnM+IDx4bXBNTTpIaXN0b3J5PiA8cmRmOlNlcT4gPHJkZjpsaSBzdEV2dDphY3Rpb249ImNyZWF0ZWQiIHN0RXZ0Omluc3RhbmNlSUQ9InhtcC5paWQ6N2YyYzUwNmUtNmE1YS1kYTRhLTk4OTItNjQ2YjM0NDBkMWU4IiBzdEV2dDp3aGVuPSIyMDIxLTA0LTA4VDE0OjU3OjAxKzEyOjAwIiBzdEV2dDpzb2Z0d2FyZUFnZW50PSJBZG9iZSBQaG90b3Nob3AgQ0MgMjAxOSAoV2luZG93cykiLz4gPHJkZjpsaSBzdEV2dDphY3Rpb249InNhdmVkIiBzdEV2dDppbnN0YW5jZUlEPSJ4bXAuaWlkOmJhM2ZjODI3LTM0ZjQtYjU0OC05ZGFiLTZhMTZlZmQzZjAxMSIgc3RFdnQ6d2hlbj0iMjAyMS0wNC0wOFQxNTowMTozMSsxMjowMCIgc3RFdnQ6c29mdHdhcmVBZ2VudD0iQWRvYmUgUGhvdG9zaG9wIENDIDIwMTkgKFdpbmRvd3MpIiBzdEV2dDpjaGFuZ2VkPSIvIi8+IDxyZGY6bGkgc3RFdnQ6YWN0aW9uPSJzYXZlZCIgc3RFdnQ6aW5zdGFuY2VJRD0ieG1wLmlpZDo2YTY5MmQzYi03ZTJkLTNiNGUtYTMzZC1hN2MwOTNlOGU0OTkiIHN0RXZ0OndoZW49IjIwMjEtMDQtMDhUMTY6MzM6MTArMTI6MDAiIHN0RXZ0OnNv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";
}
else
{
UseBase64Icon = false;
Icon = Path.Combine(Program.AppDataDirectory, "cache\\toast");
File.WriteAllBytes(Icon, Convert.FromBase64String(Image));
}
IPAddress broadcastIP = IPAddress.Parse("127.0.0.1");
@@ -314,13 +315,28 @@ namespace VRCX
System.Environment.Exit(0);
}
public bool checkForUpdateZip()
public bool CheckForUpdateZip()
{
if (File.Exists(Path.Combine(Program.AppDataDirectory, "update.exe")))
return true;
return false;
}
public void ExecuteAppFunction(string function, string json)
{
MainForm.Instance.Browser.ExecuteScriptAsync($"$app.{function}", json);
}
public void ExecuteVrFeedFunction(string function, string json)
{
VRCXVR._browser1.ExecuteScriptAsync($"$app.{function}", json);
}
public void ExecuteVrOverlayFunction(string function, string json)
{
VRCXVR._browser2.ExecuteScriptAsync($"$app.{function}", json);
}
public void SetStartup(bool enabled)
{
try
+28 -5
View File
@@ -1,4 +1,4 @@
// Copyright(c) 2019 pypy. All rights reserved.
// Copyright(c) 2019 pypy. All rights reserved.
//
// This work is licensed under the terms of the MIT license.
// For a copy, see <https://opensource.org/licenses/MIT>.
@@ -17,6 +17,7 @@ namespace VRCX
private DiscordRpcClient m_Client;
private Timer m_Timer;
private bool m_Active;
public static string DiscordAppId;
static Discord()
{
@@ -78,7 +79,7 @@ namespace VRCX
{
if (m_Client == null)
{
m_Client = new DiscordRpcClient("525953831020920832");
m_Client = new DiscordRpcClient(DiscordAppId);
if (m_Client.Initialize() == false)
{
m_Client.Dispose();
@@ -126,7 +127,7 @@ namespace VRCX
}
}
public void SetAssets(string largeKey, string largeText, string smallKey, string smallText)
public void SetAssets(string largeKey, string largeText, string smallKey, string smallText, string partyId, int partySize, int partyMax, string buttonText, string buttonUrl, string appId)
{
m_Lock.EnterWriteLock();
try
@@ -139,13 +140,35 @@ namespace VRCX
else
{
if (m_Presence.Assets == null)
{
m_Presence.Assets = new Assets();
}
if (m_Presence.Party == null)
m_Presence.Party = new Party();
m_Presence.Assets.LargeImageKey = largeKey;
m_Presence.Assets.LargeImageText = largeText;
m_Presence.Assets.SmallImageKey = smallKey;
m_Presence.Assets.SmallImageText = smallText;
m_Presence.Party.ID = partyId;
m_Presence.Party.Size = partySize;
m_Presence.Party.Max = partyMax;
Button[] Buttons = { };
if (!string.IsNullOrEmpty(buttonUrl))
{
Buttons = new Button[]
{
new Button() { Label = buttonText, Url = buttonUrl }
};
}
m_Presence.Buttons = Buttons;
if (DiscordAppId != appId)
{
DiscordAppId = appId;
if (m_Client != null)
{
m_Client.Dispose();
m_Client = null;
}
Update();
}
}
}
finally
+92 -21
View File
@@ -3,6 +3,7 @@
// This work is licensed under the terms of the MIT license.
// For a copy, see <https://opensource.org/licenses/MIT>.
using CefSharp;
using System;
using System.Collections.Generic;
using System.Globalization;
@@ -30,6 +31,8 @@ namespace VRCX
private readonly List<string[]> m_LogList;
private Thread m_Thread;
private bool m_ResetLog;
private bool m_FirstRun = true;
private static DateTime tillDate = DateTime.Now;
// NOTE
// FileSystemWatcher() is unreliable
@@ -64,6 +67,17 @@ namespace VRCX
thread.Interrupt();
thread.Join();
}
public void Reset()
{
m_ResetLog = true;
m_Thread?.Interrupt();
}
public void SetDateTill(string date)
{
tillDate = DateTime.Parse(date);
}
private void ThreadLoop()
{
@@ -86,6 +100,7 @@ namespace VRCX
{
if (m_ResetLog == true)
{
m_FirstRun = true;
m_ResetLog = false;
m_LogContextMap.Clear();
m_LogListLock.EnterWriteLock();
@@ -109,26 +124,17 @@ namespace VRCX
// sort by creation time
Array.Sort(fileInfos, (a, b) => a.CreationTimeUtc.CompareTo(b.CreationTimeUtc));
var utcNow = DateTime.UtcNow;
var minLimitDateTime = utcNow.AddDays(-7d);
var minRefreshDateTime = utcNow.AddMinutes(-3d);
foreach (var fileInfo in fileInfos)
{
var lastWriteTimeUtc = fileInfo.LastWriteTimeUtc;
if (lastWriteTimeUtc < minLimitDateTime)
fileInfo.Refresh();
if (fileInfo.Exists == false)
{
continue;
}
if (lastWriteTimeUtc >= minRefreshDateTime)
if (DateTime.Compare(fileInfo.LastWriteTime, tillDate) < 0)
{
fileInfo.Refresh();
if (fileInfo.Exists == false)
{
continue;
}
continue;
}
if (m_LogContextMap.TryGetValue(fileInfo.Name, out LogContext logContext) == true)
@@ -155,6 +161,8 @@ namespace VRCX
{
m_LogContextMap.Remove(name);
}
m_FirstRun = false;
}
private void ParseLog(FileInfo fileInfo, LogContext logContext)
@@ -184,17 +192,33 @@ namespace VRCX
continue;
}
if (DateTime.TryParseExact(
line.Substring(0, 19),
"yyyy.MM.dd HH:mm:ss",
CultureInfo.InvariantCulture,
DateTimeStyles.AssumeLocal,
out DateTime lineDate
))
{
if (DateTime.Compare(lineDate, tillDate) <= 0)
{
continue;
}
}
var offset = 34;
if (line[offset] == '[')
{
if (ParseLogOnPlayerJoinedOrLeft(fileInfo, logContext, line, offset) == true ||
ParseLogLocation(fileInfo, logContext, line, offset) == true ||
ParseLogLocationDestination(fileInfo, logContext, line, offset) == true ||
ParseLogPortalSpawn(fileInfo, logContext, line, offset) == true ||
ParseLogNotification(fileInfo, logContext, line, offset) == true ||
ParseLogJoinBlocked(fileInfo, logContext, line, offset) == true ||
ParseLogAvatarPedestalChange(fileInfo, logContext, line, offset) == true ||
ParseLogVideoError(fileInfo, logContext, line, offset) == true ||
ParseLogVideoPlay(fileInfo, logContext, line, offset) == true)
ParseLogVideoPlay(fileInfo, logContext, line, offset) == true ||
ParseLogWorldVRCX(fileInfo, logContext, line, offset) == true)
{
continue;
}
@@ -221,6 +245,12 @@ namespace VRCX
m_LogListLock.EnterWriteLock();
try
{
if (!m_FirstRun)
{
var logLine = System.Text.Json.JsonSerializer.Serialize(item);
if (MainForm.Instance != null && MainForm.Instance.Browser != null)
MainForm.Instance.Browser.ExecuteScriptAsync("$app.addGameLogEvent", logLine);
}
m_LogList.Add(item);
}
finally
@@ -244,7 +274,7 @@ namespace VRCX
dt = DateTime.UtcNow;
}
return $"{dt:yyyy'-'MM'-'dd'T'HH':'mm':'ss'Z'}";
return $"{dt:yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'fff'Z'}";
}
private bool ParseLogLocation(FileInfo fileInfo, LogContext logContext, string line, int offset)
@@ -284,6 +314,29 @@ namespace VRCX
return false;
}
private bool ParseLogLocationDestination(FileInfo fileInfo, LogContext logContext, string line, int offset)
{
// 2021.09.02 00:02:12 Log - [Behaviour] Destination set: wrld_4432ea9b-729c-46e3-8eaf-846aa0a37fdd:15609~private(usr_032383a7-748c-4fb2-94e4-bcb928e5de6b)~nonce(72CC87D420C1D49AEFFBEE8824C84B2DF0E38678E840661E)
// 2021.09.02 00:49:15 Log - [Behaviour] Destination fetching: wrld_4432ea9b-729c-46e3-8eaf-846aa0a37fdd
if (string.Compare(line, offset, "[Behaviour] Destination fetching: ", 0, 34, StringComparison.Ordinal) == 0)
{
var location = line.Substring(offset + 34);
AppendLog(new[]
{
fileInfo.Name,
ConvertLogTimeToISO8601(line),
"location-destination",
location
});
return true;
}
return false;
}
private bool ParseLogOnPlayerJoinedOrLeft(FileInfo fileInfo, LogContext logContext, string line, int offset)
{
// 2020.10.31 23:36:58 Log - [NetworkManager] OnPlayerJoined pypy
@@ -515,6 +568,28 @@ namespace VRCX
return true;
}
private bool ParseLogWorldVRCX(FileInfo fileInfo, LogContext logContext, string line, int offset)
{
// [VRCX] VideoPlay(PyPyDance) "https://jd.pypy.moe/api/v1/videos/-Q3pdlsQxOk.mp4",0.5338666,260.6938,"1339 : Le Freak (Random)"
if (string.Compare(line, offset, "[VRCX] ", 0, 7, StringComparison.Ordinal) == 0)
{
var data = line.Substring(offset + 7);
AppendLog(new[]
{
fileInfo.Name,
ConvertLogTimeToISO8601(line),
"vrcx",
data
});
return true;
}
return false;
}
private bool ParseLogSDK2VideoPlay(FileInfo fileInfo, LogContext logContext, string line, int offset)
{
// 2021.04.23 13:12:25 Log - User Natsumi-sama added URL https://www.youtube.com/watch?v=dQw4w9WgXcQ
@@ -579,14 +654,10 @@ namespace VRCX
return true;
}
public void Reset()
{
m_ResetLog = true;
m_Thread?.Interrupt();
}
public string[][] Get()
{
Update();
if (m_ResetLog == false &&
m_LogList.Count > 0)
{
+2 -2
View File
@@ -34,8 +34,8 @@ namespace VRCX
private Device _device;
private Texture2D _texture1;
private Texture2D _texture2;
private OffScreenBrowser _browser1;
private OffScreenBrowser _browser2;
public static OffScreenBrowser _browser1;
public static OffScreenBrowser _browser2;
private bool _active;
static VRCXVR()
+4
View File
@@ -147,6 +147,10 @@ namespace VRCX
{
request.UserAgent = value;
}
else if (string.Compare(key, "Referer", StringComparison.OrdinalIgnoreCase) == 0)
{
request.Referer = value;
}
else
{
request.Headers.Add(key, value);
+1667 -768
View File
File diff suppressed because it is too large Load Diff
+123 -56
View File
@@ -173,6 +173,7 @@ html
span(v-else-if="scope.row.status === 'busy'") Do Not Disturb
span(v-else) Offline
i.x-user-status(:class="statusClass(scope.row.status)")
span
span(v-text="scope.row.statusDescription")
template(v-else-if="scope.row.type === 'Avatar'")
avatar-info(:imageurl="scope.row.currentAvatarImageUrl" :userid="scope.row.userId" :hintownerid="scope.row.ownerId" :hintavatarname="scope.row.avatarName")
@@ -183,10 +184,10 @@ html
template(#tool)
div(style="margin:0 0 10px;display:flex;align-items:center")
el-select(v-model="gameLogTable.filters[0].value" @change="saveTableFilters" multiple clearable collapse-tags style="flex:1" placeholder="Filter")
el-option(v-once v-for="type in ['Location', 'OnPlayerJoined', 'OnPlayerLeft', 'PortalSpawn', 'Event', 'VideoPlay']" :key="type" :label="type" :value="type")
el-option(v-once v-for="type in ['Location', 'OnPlayerJoined', 'OnPlayerLeft', 'PortalSpawn', 'AvatarChange', 'Event', 'VideoPlay']" :key="type" :label="type" :value="type")
el-input(v-model="gameLogTable.filters[1].value" placeholder="Search" style="flex:none;width:150px;margin:0 10px")
el-tooltip(placement="bottom" content="Reset game log" :disabled="hideTooltips")
el-button(type="default" @click="resetGameLog()" icon="el-icon-refresh" circle style="flex:none")
el-tooltip(placement="bottom" content="Reload game log" :disabled="hideTooltips")
el-button(type="default" @click="refreshEntireGameLog" icon="el-icon-refresh" circle style="flex:none")
el-table-column(label="Date" prop="created_at" sortable="custom" width="90")
template(v-once #default="scope")
el-tooltip(placement="right")
@@ -194,17 +195,28 @@ html
span {{ scope.row.created_at | formatDate('YYYY-MM-DD HH24:MI:SS') }}
span {{ scope.row.created_at | formatDate('MM-DD HH24:MI') }}
el-table-column(label="Type" prop="type" width="120")
template(v-once #default="scope")
span.x-link(v-if="scope.row.location && scope.row.type !== 'Location'" v-text="scope.row.type" @click="showWorldDialog(scope.row.location)")
span(v-else v-text="scope.row.type")
el-table-column(label="User" prop="displayName" width="160")
template(v-once #default="scope")
span.x-link(v-text="scope.row.displayName" @click="lookupUser(scope.row)")
el-table-column(label="Detail" prop="data")
template(v-once #default="scope")
location(v-if="scope.row.type === 'Location'" :location="scope.row.data[0]" :hint="scope.row.data[1]")
location(v-if="scope.row.type === 'Location'" :location="scope.row.location" :hint="scope.row.worldName")
location(v-else-if="scope.row.type === 'PortalSpawn'" :location="scope.row.instanceId" :hint="scope.row.worldName")
template(v-else-if="scope.row.type === 'AvatarChange'")
span.x-link(@click="showUserDialog(scope.row.authorId)" v-text="scope.row.name")
template(v-if="scope.row.description && scope.row.name !== scope.row.description")
| - {{ scope.row.description }}
template(v-else-if="scope.row.type === 'Event'")
span(v-text="scope.row.data")
template(v-else-if="scope.row.type === 'VideoPlay'")
span.x-link(v-text="scope.row.data" @click="openExternalLink(scope.row.data)")
template(v-if="scope.row.displayName")
span.x-link(@click="lookupUser(scope.row.displayName)") ({{ scope.row.displayName }})
template(v-else-if="scope.row.type === 'Notification'")
span.x-link(v-else v-text="scope.row.data" @click="lookupUser(scope.row.data)")
span(v-if="scope.row.videoId") {{ scope.row.videoId }}:
span.x-link(v-if="scope.row.videoName" @click="openExternalLink(scope.row.videoUrl)" v-text="scope.row.videoName")
span.x-link(v-else @click="openExternalLink(scope.row.videoUrl)" v-text="scope.row.videoUrl")
template(v-else-if="scope.row.type === 'Notification' || scope.row.type === 'OnPlayerJoined' || scope.row.type === 'OnPlayerLeft'")
span.x-link(v-else v-text="scope.row.data")
//- search
.x-container(v-show="$refs.menu && $refs.menu.activeIndex === 'search'")
@@ -458,15 +470,26 @@ html
span(v-else-if='scope.row.details && scope.row.details.inviteMessage' v-text="scope.row.details.inviteMessage")
span(v-else-if='scope.row.details && scope.row.details.requestMessage' v-text="scope.row.details.requestMessage")
span(v-else-if='scope.row.details && scope.row.details.responseMessage' v-text="scope.row.details.responseMessage")
el-table-column(label="Action" width="80" align="right")
el-table-column(label="Action" width="100" align="right")
template(v-once #default="scope")
template(v-if="scope.row.senderUserId !== API.currentUser.id")
el-button(v-if="scope.row.type === 'friendRequest'" type="text" icon="el-icon-check" size="mini" @click="acceptNotification(scope.row)")
el-button(v-else-if="scope.row.type === 'invite'" type="text" icon="el-icon-chat-line-square" size="mini" @click="showSendInviteResponseDialog(scope.row)")
template(v-if="scope.row.senderUserId !== API.currentUser.id && !scope.row.$isExpired")
template(v-if="scope.row.type === 'friendRequest'")
el-tooltip(placement="top" content="Accept" :disabled="hideTooltips")
el-button(type="text" icon="el-icon-check" size="mini" @click="acceptNotification(scope.row)")
template(v-else-if="scope.row.type === 'invite'")
el-tooltip(placement="top" content="Decline with message" :disabled="hideTooltips")
el-button(type="text" icon="el-icon-chat-line-square" size="mini" @click="showSendInviteResponseDialog(scope.row)")
template(v-else-if="scope.row.type === 'requestInvite'")
el-button(v-if="lastLocation.location && isGameRunning && !checkCanInvite(lastLocation.location)" type="text" icon="el-icon-check" size="mini" @click="acceptRequestInvite(scope.row)")
el-button(type="text" icon="el-icon-chat-line-square" size="mini" style="margin-left:5px" @click="showSendInviteRequestResponseDialog(scope.row)")
el-button(type="text" icon="el-icon-close" size="mini" style="margin-left:5px" @click="hideNotification(scope.row)")
template(v-if="lastLocation.location && isGameRunning && !checkCanInvite(lastLocation.location)")
el-tooltip(placement="top" content="Invite" :disabled="hideTooltips")
el-button(type="text" icon="el-icon-check" size="mini" @click="acceptRequestInvite(scope.row)")
el-tooltip(placement="top" content="Decline with message" :disabled="hideTooltips")
el-button(type="text" icon="el-icon-chat-line-square" size="mini" style="margin-left:5px" @click="showSendInviteRequestResponseDialog(scope.row)")
template(v-if="scope.row.type !== 'requestInviteResponse' && scope.row.type !== 'inviteResponse' && scope.row.type !== 'message'")
el-tooltip(placement="top" content="Decline" :disabled="hideTooltips")
el-button(type="text" icon="el-icon-close" size="mini" style="margin-left:5px" @click="hideNotification(scope.row)")
el-tooltip(placement="top" content="Delete log" :disabled="hideTooltips")
el-button(type="text" icon="el-icon-delete" size="mini" style="margin-left:5px" @click="deleteNotificationLog(scope.row)")
//- profile
.x-container(v-show="$refs.menu && $refs.menu.activeIndex === 'profile'")
@@ -696,10 +719,10 @@ html
el-radio-button(label="dark") Dark
div.options-container-item
span.name VRCPlus Profile Icons
el-switch(v-model="displayVRCPlusIconsAsAvatar")
el-switch(v-model="displayVRCPlusIconsAsAvatar" @change="saveOpenVROption")
div.options-container-item
span.name Disable Tooltips
el-switch(v-model="hideTooltips")
el-switch(v-model="hideTooltips" @change="saveOpenVROption")
div.options-container
span.header Side Panel
br
@@ -766,10 +789,16 @@ html
span * Only works when VRChat is running.
div.options-container-item
span.name Enable
el-switch(v-model="discordActive")
el-switch(v-model="discordActive" @change="saveDiscordOption")
div.options-container-item
span.name Instance details
el-switch(v-model="discordInstance" :disabled="!discordActive")
span.name Instance type/player count
el-switch(v-model="discordInstance" @change="saveDiscordOption" :disabled="!discordActive")
div.options-container-item
span.name Join button (public only)
el-switch(v-model="discordJoinButton" @change="saveDiscordOption" :disabled="!discordActive")
div.options-container-item
span.name Hide world details in private
el-switch(v-model="discordHideInvite" @change="saveDiscordOption" :disabled="!discordActive")
div.options-container
span.header SteamVR Overlay
div.options-container-item
@@ -782,61 +811,64 @@ html
br
div.options-container-item
span.name Enable
el-switch(v-model="openVR")
el-switch(v-model="openVR" @change="saveOpenVROption")
div.options-container-item
span.name Force Run (Opens SteamVR)
el-switch(v-model="openVRAlways" :disabled="!openVR")
el-switch(v-model="openVRAlways" @change="saveOpenVROption" :disabled="!openVR")
div.options-container-item
span.name Hide Private Worlds
el-switch(v-model="hidePrivateFromFeed")
el-switch(v-model="hidePrivateFromFeed" @change="saveOpenVROption")
br
span.sub-header Wrist Feed
div.options-container-item
span.name Wrist Feed Overlay
el-switch(v-model="overlayWrist" :disabled="!openVR")
el-switch(v-model="overlayWrist" @change="saveOpenVROption" :disabled="!openVR")
div.options-container-item
span.name(style="min-width:137px") Overlay Button
el-switch(v-model="overlaybutton" inactive-text="Grip" active-text="Menu" :disabled="!openVR || !overlayWrist")
el-switch(v-model="overlaybutton" @change="saveOpenVROption" inactive-text="Grip" active-text="Menu" :disabled="!openVR || !overlayWrist")
div.options-container-item
span.name Background Color
el-switch(v-model="vrBackgroundEnabled" :disabled="!openVR || !overlayWrist")
el-switch(v-model="vrBackgroundEnabled" @change="saveOpenVROption" :disabled="!openVR || !overlayWrist")
div.options-container-item
span.name Minimal Feed Icons
el-switch(v-model="minimalFeed" :disabled="!openVR || !overlayWrist")
el-switch(v-model="minimalFeed" @change="saveOpenVROption" :disabled="!openVR || !overlayWrist")
div.options-container-item
span.name Hide VR Devices
el-switch(v-model="hideDevicesFromFeed" :disabled="!openVR || !overlayWrist")
el-switch(v-model="hideDevicesFromFeed" @change="saveOpenVROption" :disabled="!openVR || !overlayWrist")
div.options-container-item
el-button(size="small" icon="el-icon-notebook-2" @click="showWristFeedFiltersDialog()" :disabled="!openVR || !overlayWrist") Wrist Feed Filters
el-button(size="small" icon="el-icon-notebook-2" @click="showWristFeedFiltersDialog" :disabled="!openVR || !overlayWrist") Wrist Feed Filters
br
span.sub-header Notifications
div.options-container-item
span.name Overlay Notifications
el-switch(v-model="overlayNotifications" :disabled="!openVR")
el-switch(v-model="overlayNotifications" @change="saveOpenVROption" :disabled="!openVR")
div.options-container-item
el-button(size="small" icon="el-icon-rank" @click="showNotificationPositionDialog()" :disabled="!overlayNotifications || !openVR") Notification Position
el-button(size="small" icon="el-icon-rank" @click="showNotificationPositionDialog" :disabled="!overlayNotifications || !openVR") Notification Position
div.options-container-item
span.name XSOverlay Notifications
el-switch(v-model="xsNotifications")
el-switch(v-model="xsNotifications" @change="saveOpenVROption")
div.options-container-item
el-button(size="small" icon="el-icon-time" @click="promptNotificationTimeout()" :disabled="(!overlayNotifications || !openVR) && !xsNotifications") Notification Timeout
span.name User images (slower)
el-switch(v-model="imageNotifications" @change="saveOpenVROption")
div.options-container-item
el-button(size="small" icon="el-icon-time" @click="promptNotificationTimeout" :disabled="(!overlayNotifications || !openVR) && !xsNotifications") Notification Timeout
div.options-container-item
span.name Desktop Notifications, When to display:
br
el-radio-group(v-model="desktopToast" size="mini")
el-radio-group(v-model="desktopToast" @change="saveOpenVROption" size="mini")
el-radio-button(label="Never")
el-radio-button(label="Inside VR")
el-radio-button(label="Game Closed")
el-radio-button(label="Game Running")
el-radio-button(label="Always")
div.options-container-item
el-button(size="small" icon="el-icon-chat-square" @click="showNotyFeedFiltersDialog()") Notification Filters
el-button(size="small" icon="el-icon-chat-square" @click="showNotyFeedFiltersDialog") Notification Filters
br
span.sub-header Text-To-Speech Options
div.options-container-item
span.name Notification TTS, When to play:
br
el-radio-group(v-model="notificationTTS" size="mini")
el-radio-group(v-model="notificationTTS" @change="saveNotificationTTS" size="mini")
el-radio-button(label="Never")
el-radio-button(label="Inside VR")
el-radio-button(label="Game Closed")
@@ -857,31 +889,30 @@ html
div.options-container-item
span.name Download on invite:
br
el-radio-group(v-model="worldAutoCacheInvite" size="mini")
el-radio-group(v-model="worldAutoCacheInvite" @change="saveOpenVROption" size="mini")
el-radio-button(label="Never")
el-radio-button(label="Game Closed")
el-radio-button(label="Game Running")
el-radio-button(label="Always")
div.options-container-item
el-switch(v-model="worldAutoCacheInviteFilter" inactive-text="VIP" active-text="Everyone" :disabled="worldAutoCacheInvite == 'Never'")
el-switch(v-model="worldAutoCacheInviteFilter" @change="saveOpenVROption" inactive-text="VIP" active-text="Everyone" :disabled="worldAutoCacheInvite === 'Never'")
div.options-container-item
span.name Download on GPS:
br
el-radio-group(v-model="worldAutoCacheGPS" size="mini")
el-radio-group(v-model="worldAutoCacheGPS" @change="saveOpenVROption" size="mini")
el-radio-button(label="Never")
el-radio-button(label="Game Closed")
el-radio-button(label="Game Running")
el-radio-button(label="Always")
div.options-container-item
el-switch(v-model="worldAutoCacheGPSFilter" inactive-text="VIP" active-text="Everyone" :disabled="worldAutoCacheGPS == 'Never'")
el-switch(v-model="worldAutoCacheGPSFilter" @change="saveOpenVROption" inactive-text="VIP" active-text="Everyone" :disabled="worldAutoCacheGPS === 'Never'")
div.options-container-item
el-button-group
el-button(size="small" icon="el-icon-download" @click="showDownloadDialog()") Download History
el-button(size="small" icon="el-icon-download" @click="showDownloadDialog") Download History
br
span.sub-header Automatically Manage Cache When Closing VRChat
div.options-container-item
span.name(style="min-width:300px") Auto delete old versions from cache
el-switch(v-model="autoSweepVRChatCache")
el-switch(v-model="autoSweepVRChatCache" @change="saveOpenVROption")
div.options-container
span.header Application
div.options-container-item
@@ -900,6 +931,13 @@ html
el-button-group
el-button(size="small" icon="el-icon-s-operation" @click="showLaunchOptions()") Launch Options
el-button(size="small" icon="el-icon-s-operation" @click="showVRChatConfig()") VRChat config.json
div.options-container
span.header YouTube API
div.options-container-item
span.name Enabled
el-switch(v-model="youTubeApi" @change="changeYouTubeApi")
div.options-container-item
el-button(size="small" icon="el-icon-caret-right" @click="showYouTubeApiDialog") YouTube API Key
div.options-container(style="margin-top:45px;border-top:1px solid #eee;padding-top:30px")
span.header Legal Notice
div.options-container-item
@@ -1053,6 +1091,7 @@ html
el-button(:type="(userDialog.incomingRequest || userDialog.outgoingRequest) ? 'success' : (userDialog.isBlock || userDialog.isMute || userDialog.isHideAvatar) ? 'danger' : 'default'" icon="el-icon-more" circle style="margin-left:5px")
el-dropdown-menu(#default="dropdown")
el-dropdown-item(icon="el-icon-refresh" command="Refresh") Refresh
el-dropdown-item(icon="el-icon-s-order" command="Copy User") Copy User URL
template(v-if="userDialog.ref.id === API.currentUser.id")
el-dropdown-item(icon="el-icon-picture-outline" command="Manage Gallery" divided) Manage Gallery/Icons
el-dropdown-item(icon="el-icon-s-custom" command="Show Avatar Author") Show Avatar Author
@@ -1075,7 +1114,7 @@ html
el-dropdown-item(v-else icon="el-icon-plus" command="Send Friend Request") Send Friend Request
el-dropdown-item(icon="el-icon-s-custom" command="Show Avatar Author" divided) Show Avatar Author
el-dropdown-item(icon="el-icon-s-custom" command="Show Fallback Avatar Details") Show Fallback Avatar Details
el-dropdown-item(v-if="userDialog.currentAvatarImageUrl !== 'https://assets.vrchat.com/system/defaultAvatar.png'" icon="el-icon-picture-outline" command="Previous Images") Show Avatar Previous Images
el-dropdown-item(v-if="userDialog.ref.currentAvatarImageUrl !== 'https://assets.vrchat.com/system/defaultAvatar.png'" icon="el-icon-picture-outline" command="Previous Images") Show Avatar Previous Images
el-dropdown-item(v-if="userDialog.isBlock" icon="el-icon-circle-check" command="Unblock" divided style="color:#F56C6C") Unblock
el-dropdown-item(v-else icon="el-icon-circle-close" command="Block" divided :disabled="userDialog.ref.$isModerator") Block
el-dropdown-item(v-if="userDialog.isMute" icon="el-icon-microphone" command="Unmute" style="color:#F56C6C") Unmute
@@ -1665,6 +1704,16 @@ html
el-button(size="small" @click="VRChatConfigDialog.visible = false") Cancel
el-button(type="primary" size="small" :disabled="VRChatConfigDialog.loading" @click="saveVRChatConfigFile") Save
//- dialog: YouTube Api Dialog
el-dialog.x-dialog(ref="youTubeApiDialog" :visible.sync="youTubeApiDialog.visible" title="YouTube API" width="400px")
div(style='font-size:12px;')
| Enter your YouTube API Key (optional) #[br]
el-input(type="textarea" v-model="youTubeApiKey" placeholder="YouTube API Key" maxlength="39" show-word-limit style="dispaly:block;margin-top:10px")
template(#footer)
div(style="display:flex")
el-button(size="small" @click="openExternalLink('https://rapidapi.com/blog/how-to-get-youtube-api-key/')") Guide
el-button(type="primary" size="small" @click="testYouTubeApiKey" style="margin-left:auto") Save
//- dialog: Cache Download
el-dialog.x-dialog(ref="downloadDialog" :visible.sync="downloadDialog.visible" title="Download History" width="770px")
div(v-if="downloadInProgress && downloadCurrent.ref")
@@ -1769,7 +1818,7 @@ html
el-button(type="primary" size="small" style="margin-left:auto" @click="notificationPositionDialog.visible = false") OK
//- dialog: Noty feed filters
el-dialog.x-dialog(ref="notyFeedFiltersDialog" :visible.sync="notyFeedFiltersDialog.visible" title="Notification Filters" width="480px")
el-dialog.x-dialog(ref="notyFeedFiltersDialog" :visible.sync="notyFeedFiltersDialog.visible" title="Notification Filters" width="500px")
.toggle-list
.toggle-item
span.toggle-name OnPlayerJoining
@@ -1868,21 +1917,30 @@ html
el-radio-button(label="Friends")
.toggle-item
span.toggle-name Portal Spawn
el-tooltip(placement="top" style="margin-left:5px" content="Requires '--enable-sdk-log-levels' steam launch option")
i.el-icon-warning
el-radio-group(v-model="sharedFeedFilters.noty.PortalSpawn" size="mini")
el-radio-button(label="Off")
el-radio-button(label="VIP")
el-radio-button(label="Friends")
el-radio-button(label="Everyone")
.toggle-item
span.toggle-name Events
el-radio-group(v-model="sharedFeedFilters.noty.Event" size="mini")
el-radio-button(label="Off")
el-radio-button(label="On")
//- .toggle-item
//- span.toggle-name Avatar Change
//- el-radio-group(v-model="sharedFeedFilters.noty.AvatarChange" size="mini")
//- el-radio-button(label="Off")
//- el-radio-button(label="VIP")
//- el-radio-button(label="Friends")
//- el-radio-button(label="Everyone")
.toggle-item
span.toggle-name Video Play
el-radio-group(v-model="sharedFeedFilters.noty.VideoPlay" size="mini")
el-radio-button(label="Off")
el-radio-button(label="On")
.toggle-item
span.toggle-name Events
el-radio-group(v-model="sharedFeedFilters.noty.Event" size="mini")
el-radio-button(label="Off")
el-radio-button(label="On")
.toggle-item
span.toggle-name Blocked Player Joins
el-radio-group(v-model="sharedFeedFilters.noty.BlockedOnPlayerJoined" size="mini")
@@ -1916,7 +1974,7 @@ html
el-button(type="primary" size="small" style="margin-left:10px" @click="saveSharedFeedFilters") Save
//- dialog: wrist feed filters
el-dialog.x-dialog(ref="wristFeedFiltersDialog" :visible.sync="wristFeedFiltersDialog.visible" title="Wrist Feed Filters" width="480px")
el-dialog.x-dialog(ref="wristFeedFiltersDialog" :visible.sync="wristFeedFiltersDialog.visible" title="Wrist Feed Filters" width="500px")
.toggle-list
.toggle-item
span.toggle-name Self Location
@@ -2020,19 +2078,28 @@ html
el-radio-button(label="Friends")
.toggle-item
span.toggle-name Portal Spawn
el-tooltip(placement="top" style="margin-left:5px" content="Requires '--enable-sdk-log-levels' steam launch option")
i.el-icon-warning
el-radio-group(v-model="sharedFeedFilters.wrist.PortalSpawn" size="mini")
el-radio-button(label="Off")
el-radio-button(label="VIP")
el-radio-button(label="Friends")
el-radio-button(label="Everyone")
//- .toggle-item
//- span.toggle-name Avatar Change
//- el-radio-group(v-model="sharedFeedFilters.wrist.AvatarChange" size="mini")
//- el-radio-button(label="Off")
//- el-radio-button(label="VIP")
//- el-radio-button(label="Friends")
//- el-radio-button(label="Everyone")
.toggle-item
span.toggle-name Events
el-radio-group(v-model="sharedFeedFilters.wrist.Event" size="mini")
span.toggle-name Video Play
el-radio-group(v-model="sharedFeedFilters.wrist.VideoPlay" size="mini")
el-radio-button(label="Off")
el-radio-button(label="On")
.toggle-item
span.toggle-name Video Play
el-radio-group(v-model="sharedFeedFilters.noty.VideoPlay" size="mini")
span.toggle-name Events
el-radio-group(v-model="sharedFeedFilters.wrist.Event" size="mini")
el-radio-button(label="Off")
el-radio-button(label="On")
.toggle-item
+275 -1
View File
@@ -1,7 +1,7 @@
import sqliteService from '../service/sqlite.js';
class Database {
async init(userId) {
async initUserTables(userId) {
Database.userId = userId.replaceAll('-', '').replaceAll('_', '');
await sqliteService.executeNonQuery(
`CREATE TABLE IF NOT EXISTS ${Database.userId}_feed_gps (id INTEGER PRIMARY KEY, created_at TEXT, user_id TEXT, display_name TEXT, location TEXT, world_name TEXT, previous_location TEXT, time INTEGER)`
@@ -21,11 +21,32 @@ class Database {
await sqliteService.executeNonQuery(
`CREATE TABLE IF NOT EXISTS ${Database.userId}_friend_log_history (id INTEGER PRIMARY KEY, created_at TEXT, type TEXT, user_id TEXT, display_name TEXT, previous_display_name TEXT, trust_level TEXT, previous_trust_level TEXT)`
);
await sqliteService.executeNonQuery(
`CREATE TABLE IF NOT EXISTS ${Database.userId}_notifications (id TEXT PRIMARY KEY, created_at TEXT, type TEXT, sender_user_id TEXT, sender_username TEXT, receiver_user_id TEXT, message TEXT, world_id TEXT, world_name TEXT, image_url TEXT, invite_message TEXT, request_message TEXT, response_message TEXT, expired INTEGER)`
);
await sqliteService.executeNonQuery(
`CREATE TABLE IF NOT EXISTS memos (user_id TEXT PRIMARY KEY, edited_at TEXT, memo TEXT)`
);
}
async initTables() {
await sqliteService.executeNonQuery(
`CREATE TABLE IF NOT EXISTS gamelog_location (id INTEGER PRIMARY KEY, created_at TEXT, location TEXT, world_id TEXT, world_name TEXT, time INTEGER, UNIQUE(created_at, location))`
);
await sqliteService.executeNonQuery(
`CREATE TABLE IF NOT EXISTS gamelog_join_leave (id INTEGER PRIMARY KEY, created_at TEXT, type TEXT, display_name TEXT, location TEXT, user_id TEXT, time INTEGER, UNIQUE(created_at, type, display_name))`
);
await sqliteService.executeNonQuery(
`CREATE TABLE IF NOT EXISTS gamelog_portal_spawn (id INTEGER PRIMARY KEY, created_at TEXT, display_name TEXT, location TEXT, user_id TEXT, instance_id TEXT, world_name TEXT, UNIQUE(created_at, display_name))`
);
await sqliteService.executeNonQuery(
`CREATE TABLE IF NOT EXISTS gamelog_video_play (id INTEGER PRIMARY KEY, created_at TEXT, video_url TEXT, video_name TEXT, video_id TEXT, location TEXT, display_name TEXT, user_id TEXT, UNIQUE(created_at, video_url))`
);
await sqliteService.executeNonQuery(
`CREATE TABLE IF NOT EXISTS gamelog_event (id INTEGER PRIMARY KEY, created_at TEXT, data TEXT, UNIQUE(created_at, data))`
);
}
async getFeedDatabase() {
var feedDatabase = [];
var date = new Date();
@@ -349,6 +370,259 @@ class Database {
}
);
}
async getGamelogDatabase() {
var gamelogDatabase = [];
var date = new Date();
date.setDate(date.getDate() - 7); // 7 day limit
var dateOffset = date.toJSON();
await sqliteService.execute((dbRow) => {
var row = {
rowId: dbRow[0],
created_at: dbRow[1],
type: 'Location',
location: dbRow[2],
worldId: dbRow[3],
worldName: dbRow[4],
time: dbRow[5]
};
gamelogDatabase.unshift(row);
}, `SELECT * FROM gamelog_location WHERE created_at >= date('${dateOffset}')`);
await sqliteService.execute((dbRow) => {
var row = {
rowId: dbRow[0],
created_at: dbRow[1],
type: dbRow[2],
displayName: dbRow[3],
location: dbRow[4],
userId: dbRow[5],
time: dbRow[6]
};
gamelogDatabase.unshift(row);
}, `SELECT * FROM gamelog_join_leave WHERE created_at >= date('${dateOffset}')`);
await sqliteService.execute((dbRow) => {
var row = {
rowId: dbRow[0],
created_at: dbRow[1],
type: 'PortalSpawn',
displayName: dbRow[2],
location: dbRow[3],
userId: dbRow[4],
instanceId: dbRow[5],
worldName: dbRow[6]
};
gamelogDatabase.unshift(row);
}, `SELECT * FROM gamelog_portal_spawn WHERE created_at >= date('${dateOffset}')`);
await sqliteService.execute((dbRow) => {
var row = {
rowId: dbRow[0],
created_at: dbRow[1],
type: 'VideoPlay',
videoUrl: dbRow[2],
videoName: dbRow[3],
videoId: dbRow[4],
location: dbRow[5],
displayName: dbRow[6],
userId: dbRow[7]
};
gamelogDatabase.unshift(row);
}, `SELECT * FROM gamelog_video_play WHERE created_at >= date('${dateOffset}')`);
await sqliteService.execute((dbRow) => {
var row = {
rowId: dbRow[0],
created_at: dbRow[1],
type: 'Event',
data: dbRow[2]
};
gamelogDatabase.unshift(row);
}, `SELECT * FROM gamelog_event WHERE created_at >= date('${dateOffset}')`);
var compareByCreatedAt = function (a, b) {
var A = a.created_at;
var B = b.created_at;
if (A < B) {
return -1;
}
if (A > B) {
return 1;
}
return 0;
};
gamelogDatabase.sort(compareByCreatedAt);
return gamelogDatabase;
}
addGamelogLocationToDatabase(entry) {
sqliteService.executeNonQuery(
`INSERT OR IGNORE INTO gamelog_location (created_at, location, world_id, world_name, time) VALUES (@created_at, @location, @world_id, @world_name, @time)`,
{
'@created_at': entry.created_at,
'@location': entry.location,
'@world_id': entry.worldId,
'@world_name': entry.worldName,
'@time': entry.time
}
);
}
updateGamelogLocationTimeToDatabase(entry) {
sqliteService.executeNonQuery(
`UPDATE gamelog_location SET time = @time WHERE created_at = @created_at`,
{
'@created_at': entry.created_at,
'@time': entry.time
}
);
}
addGamelogJoinLeaveToDatabase(entry) {
sqliteService.executeNonQuery(
`INSERT OR IGNORE INTO gamelog_join_leave (created_at, type, display_name, location, user_id, time) VALUES (@created_at, @type, @display_name, @location, @user_id, @time)`,
{
'@created_at': entry.created_at,
'@type': entry.type,
'@display_name': entry.displayName,
'@location': entry.location,
'@user_id': entry.userId,
'@time': entry.time
}
);
}
addGamelogPortalSpawnToDatabase(entry) {
sqliteService.executeNonQuery(
`INSERT OR IGNORE INTO gamelog_portal_spawn (created_at, display_name, location, user_id, instance_id, world_name) VALUES (@created_at, @display_name, @location, @user_id, @instance_id, @world_name)`,
{
'@created_at': entry.created_at,
'@display_name': entry.displayName,
'@location': entry.location,
'@user_id': entry.userId,
'@instance_id': entry.instanceId,
'@world_name': entry.worldName
}
);
}
addGamelogVideoPlayToDatabase(entry) {
sqliteService.executeNonQuery(
`INSERT OR IGNORE INTO gamelog_video_play (created_at, video_url, video_name, video_id, location, display_name, user_id) VALUES (@created_at, @video_url, @video_name, @video_id, @location, @display_name, @user_id)`,
{
'@created_at': entry.created_at,
'@video_url': entry.videoUrl,
'@video_name': entry.videoName,
'@video_id': entry.videoId,
'@location': entry.location,
'@display_name': entry.displayName,
'@user_id': entry.userId
}
);
}
addGamelogEventToDatabase(entry) {
sqliteService.executeNonQuery(
`INSERT OR IGNORE INTO gamelog_event (created_at, data) VALUES (@created_at, @data)`,
{
'@created_at': entry.created_at,
'@data': entry.data
}
);
}
async getNotifications() {
var notifications = [];
await sqliteService.execute((dbRow) => {
var row = {
id: dbRow[0],
created_at: dbRow[1],
type: dbRow[2],
senderUserId: dbRow[3],
senderUsername: dbRow[4],
receiverUserId: dbRow[5],
message: dbRow[6],
details: {
worldId: dbRow[7],
worldName: dbRow[8],
imageUrl: dbRow[9],
inviteMessage: dbRow[10],
requestMessage: dbRow[11],
responseMessage: dbRow[12]
}
};
row.$isExpired = false;
if (dbRow[13] === 1) {
row.$isExpired = true;
}
notifications.unshift(row);
}, `SELECT * FROM ${Database.userId}_notifications LIMIT 5000`);
return notifications;
}
addNotificationToDatabase(row) {
var entry = {
id: '',
created_at: '',
type: '',
senderUserId: '',
senderUsername: '',
receiverUserId: '',
message: '',
...row,
details: {
worldId: '',
worldName: '',
imageUrl: '',
inviteMessage: '',
requestMessage: '',
responseMessage: '',
...row.details
}
};
var expired = 0;
if (row.$isExpired) {
expired = 1;
}
sqliteService.executeNonQuery(
`INSERT OR IGNORE INTO ${Database.userId}_notifications (id, created_at, type, sender_user_id, sender_username, receiver_user_id, message, world_id, world_name, image_url, invite_message, request_message, response_message, expired) VALUES (@id, @created_at, @type, @sender_user_id, @sender_username, @receiver_user_id, @message, @world_id, @world_name, @image_url, @invite_message, @request_message, @response_message, @expired)`,
{
'@id': entry.id,
'@created_at': entry.created_at,
'@type': entry.type,
'@sender_user_id': entry.senderUserId,
'@sender_username': entry.senderUsername,
'@receiver_user_id': entry.receiverUserId,
'@message': entry.message,
'@world_id': entry.details.worldId,
'@world_name': entry.details.worldName,
'@image_url': entry.details.imageUrl,
'@invite_message': entry.details.inviteMessage,
'@request_message': entry.details.requestMessage,
'@response_message': entry.details.responseMessage,
'@expired': expired
}
);
}
deleteNotification(rowId) {
sqliteService.executeNonQuery(
`DELETE FROM ${Database.userId}_notifications WHERE id = @row_id`,
{
'@row_id': rowId
}
);
}
updateNotificationExpired(entry) {
var expired = 0;
if (entry.$isExpired) {
expired = 1;
}
sqliteService.executeNonQuery(
`UPDATE ${Database.userId}_notifications SET expired = @expired WHERE id = @id`,
{
'@id': entry.id,
'@expired': expired
}
);
}
}
var self = new Database();
+62 -73
View File
@@ -1,93 +1,82 @@
// requires binding of LogWatcher
// <string, object>
var contextMap = new Map();
function parseRawGameLog(dt, type, args) {
var gameLog = {
dt,
type
};
switch (type) {
case 'location':
gameLog.location = args[0];
gameLog.worldName = args[1];
break;
case 'player-joined':
gameLog.userDisplayName = args[0];
gameLog.userType = args[1];
break;
case 'player-left':
gameLog.userDisplayName = args[0];
break;
case 'notification':
gameLog.json = args[0];
break;
case 'portal-spawn':
gameLog.userDisplayName = args[0];
break;
case 'event':
gameLog.event = args[0];
break;
case 'video-play':
gameLog.videoURL = args[0];
gameLog.displayName = args[1];
break;
default:
break;
}
return gameLog;
}
class GameLogService {
async poll() {
var rawGameLogs = await LogWatcher.Get();
var gameLogs = [];
var now = Date.now();
parseRawGameLog(dt, type, args) {
var gameLog = {
dt,
type
};
for (var [fileName, dt, type, ...args] of rawGameLogs) {
var context = contextMap.get(fileName);
if (typeof context === 'undefined') {
context = {
updatedAt: null,
switch (type) {
case 'location':
gameLog.location = args[0];
gameLog.worldName = args[1];
break;
// location
location: null
};
contextMap.set(fileName, context);
}
case 'location-destination':
gameLog.location = args[0];
break;
var gameLog = parseRawGameLog(dt, type, args);
case 'player-joined':
gameLog.userDisplayName = args[0];
gameLog.userType = args[1];
break;
switch (gameLog.type) {
case 'location':
context.location = gameLog.location;
break;
case 'player-left':
gameLog.userDisplayName = args[0];
break;
default:
break;
}
case 'notification':
gameLog.json = args[0];
break;
context.updatedAt = now;
case 'portal-spawn':
gameLog.userDisplayName = args[0];
break;
gameLogs.push(gameLog);
case 'event':
gameLog.event = args[0];
break;
case 'video-play':
gameLog.videoUrl = args[0];
gameLog.displayName = args[1];
break;
case 'vrcx':
gameLog.data = args[0];
break;
default:
break;
}
return gameLog;
}
async getAll() {
var gameLogs = [];
var done = false;
while (!done) {
var rawGameLogs = await LogWatcher.Get();
// eslint-disable-next-line no-unused-vars
for (var [fileName, dt, type, ...args] of rawGameLogs) {
var gameLog = this.parseRawGameLog(dt, type, args);
gameLogs.push(gameLog);
}
if (rawGameLogs.length === 0) {
done = true;
}
}
return gameLogs;
}
async setDateTill(dateTill) {
await LogWatcher.SetDateTill(dateTill);
}
async reset() {
await LogWatcher.Reset();
contextMap.clear();
}
}
+221 -789
View File
File diff suppressed because it is too large Load Diff
+70 -18
View File
@@ -36,17 +36,23 @@ html
.detail
span.extra
span.time {{ feed.created_at | formatDate('HH:MI') }}
| #[span.name(v-text="feed.displayName")] #[i.x-user-status(:class="statusClass(feed.status)")] {{feed.statusDescription}}
| #[span.name(v-text="feed.displayName")]
template(v-if="feed.statusDescription === feed.previousStatusDescription")
i.x-user-status(:class="statusClass(feed.previousStatus)")
i.el-icon-right
i.x-user-status(:class="statusClass(feed.status)")
template(v-else)
| #[i.x-user-status(:class="statusClass(feed.status)")] {{feed.statusDescription}}
div(v-else-if="feed.type === 'OnPlayerJoined'" class="x-friend-item" :class="{ friend: feed.isFriend, favorite: feed.isFavorite }")
.detail
span.extra
span.time {{ feed.created_at | formatDate('HH:MI') }}
| ▶️ #[span.name(v-text="feed.data")]
| ▶️ #[span.name(v-text="feed.displayName")]
div(v-else-if="feed.type === 'OnPlayerLeft'" class="x-friend-item" :class="{ friend: feed.isFriend, favorite: feed.isFavorite }")
.detail
span.extra
span.time {{ feed.created_at | formatDate('HH:MI') }}
| ◀️ #[span.name(v-text="feed.data")]
| ◀️ #[span.name(v-text="feed.displayName")]
div(v-else-if="feed.type === 'OnPlayerJoining'" class="x-friend-item" :class="{ friend: feed.isFriend, favorite: feed.isFavorite }")
.detail
span.extra
@@ -57,7 +63,16 @@ html
.detail
span.extra
span.time {{ feed.created_at | formatDate('HH:MI') }}
location(:location="feed.data[0]" :hint="feed.data[1]")
location(:location="feed.location" :hint="feed.worldName")
div(v-else-if="feed.type === 'VideoPlay'" class="x-friend-item" :class="{ friend: feed.isFriend, favorite: feed.isFavorite }")
.detail
span.extra
span.time {{ feed.created_at | formatDate('HH:MI') }}
| 🎵 #[span.name(v-text="feed.displayName")]
template(v-if="feed.videoName")
| #[span(v-text="feed.videoName")]
template(v-else)
| #[span(v-text="feed.videoUrl")]
div(v-else-if="feed.type === 'invite'" class="x-friend-item" :class="{ friend: feed.isFriend, favorite: feed.isFavorite }")
.detail
span.extra
@@ -107,7 +122,16 @@ html
.detail
span.extra
span.time {{ feed.created_at | formatDate('HH:MI') }}
| ✨ #[span.name(v-text="feed.data")]
| ✨ #[span.name(v-text="feed.displayName")]
template(v-if="feed.worldName")
| #[location(:location="feed.instanceId" :hint="feed.worldName")]
div(v-else-if="feed.type === 'AvatarChange'" class="x-friend-item" :class="{ friend: feed.isFriend, favorite: feed.isFavorite }")
.detail
span.extra
span.time {{ feed.created_at | formatDate('HH:MI') }}
| 🧍 #[span.name(v-text="feed.displayName")] {{ feed.name }}
template(v-if="feed.description && feed.description !== feed.name")
| - {{ feed.description }}
div(v-else-if="feed.type === 'Event'" class="x-friend-item")
.detail
span.extra
@@ -159,17 +183,23 @@ html
.detail
span.extra
span.time {{ feed.created_at | formatDate('HH:MI') }}
| #[span.name(v-text="feed.displayName")] is #[i.x-user-status(:class="statusClass(feed.status)")] {{feed.statusDescription}}
| #[span.name(v-text="feed.displayName")]
template(v-if="feed.statusDescription === feed.previousStatusDescription")
i.x-user-status(:class="statusClass(feed.previousStatus)")
i.el-icon-right
i.x-user-status(:class="statusClass(feed.status)")
template(v-else)
| #[i.x-user-status(:class="statusClass(feed.status)")] {{feed.statusDescription}}
div(v-else-if="feed.type === 'OnPlayerJoined'" class="x-friend-item" :class="{ friend: feed.isFriend, favorite: feed.isFavorite }")
.detail
span.extra
span.time {{ feed.created_at | formatDate('HH:MI') }}
| #[span.name(v-text="feed.data")] has joined
| #[span.name(v-text="feed.displayName")] has joined
div(v-else-if="feed.type === 'OnPlayerLeft'" class="x-friend-item" :class="{ friend: feed.isFriend, favorite: feed.isFavorite }")
.detail
span.extra
span.time {{ feed.created_at | formatDate('HH:MI') }}
| #[span.name(v-text="feed.data")] has left
| #[span.name(v-text="feed.displayName")] has left
div(v-else-if="feed.type === 'OnPlayerJoining'" class="x-friend-item" :class="{ friend: feed.isFriend, favorite: feed.isFavorite }")
.detail
span.extra
@@ -179,7 +209,16 @@ html
.detail
span.extra
span.time {{ feed.created_at | formatDate('HH:MI') }}
location(:location="feed.data[0]" :hint="feed.data[1]")
location(:location="feed.location" :hint="feed.worldName")
div(v-else-if="feed.type === 'VideoPlay'" class="x-friend-item" :class="{ friend: feed.isFriend, favorite: feed.isFavorite }")
.detail
span.extra
span.time {{ feed.created_at | formatDate('HH:MI') }}
| #[span.name(v-text="feed.displayName")] changed video to
template(v-if="feed.videoName")
| #[span(v-text="feed.videoName")]
template(v-else)
| #[span(v-text="feed.videoUrl")]
div(v-else-if="feed.type === 'invite'" class="x-friend-item" :class="{ friend: feed.isFriend, favorite: feed.isFavorite }")
.detail
span.extra
@@ -229,7 +268,16 @@ html
.detail
span.extra
span.time {{ feed.created_at | formatDate('HH:MI') }}
| #[span.name(v-text="feed.data")] has spawned a portal
| #[span.name(v-text="feed.displayName")] has spawned a portal
template(v-if="feed.worldName")
| to #[location(:location="feed.instanceId" :hint="feed.worldName")]
div(v-else-if="feed.type === 'AvatarChange'" class="x-friend-item" :class="{ friend: feed.isFriend, favorite: feed.isFavorite }")
.detail
span.extra
span.time {{ feed.created_at | formatDate('HH:MI') }}
| #[span.name(v-text="feed.displayName")] changed into avatar {{ feed.name }}
template(v-if="feed.description && feed.description !== feed.name")
| - {{ feed.description }}
div(v-else-if="feed.type === 'Event'" class="x-friend-item")
.detail
span.extra
@@ -301,21 +349,25 @@ html
br
span {{ device[2] }}%
.x-containerbottom
template(v-if="nowPlaying.playing")
span(style="float:right;padding-left:10px") {{ nowPlaying.remainingText }}
marquee-text {{ nowPlaying.name }}
div.np-progress-bar(:style="{ width: nowPlaying.percentage + '%' }")
template(v-if="config && config.minimalFeed")
template(v-if="downloadProgress === 100")
template(v-if="config.downloadProgress === 100")
span(style="display:inline-block;margin-right:5px") #[i.el-icon-loading]
template(v-else-if="downloadProgress > 0")
span(style="display:inline-block;margin-right:5px") {{ downloadProgress }}%
template(v-if="lastLocation.date != 0")
template(v-else-if="config.downloadProgress > 0")
span(style="display:inline-block;margin-right:5px") {{ config.downloadProgress }}%
template(v-if="lastLocation.date !== 0")
span(style="float:right") {{ lastLocationTimer }}
span(style="display:inline-block") {{ lastLocation.playerList.length }}
span(style="display:inline-block;font-weight:bold") {{ lastLocation.friendList.length !== 0 ? ` (${lastLocation.friendList.length})` : ''}}
template(v-else)
template(v-if="downloadProgress === 100")
template(v-if="config.downloadProgress === 100")
span(style="display:inline-block;margin-right:5px") Downloading: #[i.el-icon-loading]
template(v-else-if="downloadProgress > 0")
span(style="display:inline-block;margin-right:5px") Downloading: {{ downloadProgress }}%
template(v-if="lastLocation.date != 0")
template(v-else-if="config.downloadProgress > 0")
span(style="display:inline-block;margin-right:5px") Downloading: {{ config.downloadProgress }}%
template(v-if="lastLocation.date !== 0")
span(style="float:right") Timer: {{ lastLocationTimer }}
span(style="display:inline-block") Players: {{ lastLocation.playerList.length }}
span(style="display:inline-block;font-weight:bold") {{ lastLocation.friendList.length !== 0 ? ` (${lastLocation.friendList.length})` : ''}}
+21 -6
View File
@@ -22,9 +22,6 @@
.noty_body {
display: block;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.noty_layout {
@@ -34,6 +31,7 @@
.noty_theme__relax.noty_bar,
.noty_theme__sunset.noty_bar {
height: 42px;
position: relative;
margin: 4px 0;
overflow: hidden;
@@ -42,9 +40,7 @@
.noty_theme__relax.noty_bar .noty_body,
.noty_theme__sunset.noty_bar .noty_body {
padding: 5px 10px 10px;
font-size: 15px;
text-align: center;
text-shadow: 1px 1px 1px rgba(0, 0, 0, 0.1);
}
@@ -143,6 +139,19 @@
opacity: 0.6;
}
.noty-text {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
padding: 8px 8px 0 11px;
}
.noty-img {
height: 42px;
float: left;
border-radius: 4px;
}
::-webkit-scrollbar {
width: 8px;
height: 8px;
@@ -202,16 +211,22 @@ button {
}
.x-containerbottom span {
padding: 0px;
display: block;
overflow: hidden;
}
.np-progress-bar {
width: 0%;
height: 2px;
background-color: white;
}
.x-friend-item {
box-sizing: border-box;
display: flex;
align-items: center;
font-size: 18px;
height: 27.1px;
}
.x-friend-item .time {