Skip to content
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
11 changes: 11 additions & 0 deletions Update.json
Original file line number Diff line number Diff line change
Expand Up @@ -3574,6 +3574,17 @@
}
],
"Notes": "Fix loginpage.php loop."
},
"3.4.6": {
"UpdateDate": 1777708421566,
"Prerelease": true,
"UpdateContents": [
{
"PR": 980,
"Description": "Switch all backend endpoints to api.xmoj-script.uk"
}
],
"Notes": "No release notes were provided for this release."
}
}
}
21 changes: 11 additions & 10 deletions XMOJ.user.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// ==UserScript==
// @name XMOJ
// @version 3.4.5
// @version 3.4.6
// @description XMOJ增强脚本
// @author @XMOJ-Script-dev, @langningchen and the community
// @namespace https://github/langningchen
Expand All @@ -21,10 +21,11 @@
// @grant GM_setValue
// @grant GM_getValue
// @grant GM_cookie
// @homepage https://www.xmoj-bbs.me/
// @supportURL https://support.xmoj-bbs.me/form/8050213e-c806-4680-b414-0d1c48263677
// @homepage https://www.xmoj-script.uk/
// @supportURL https://github.com/XMOJ-Script-dev/XMOJ-Script/issues
// @connect api.xmoj-bbs.tech
// @connect api.xmoj-bbs.me
// @connect api.xmoj-script.uk
// @connect challenges.cloudflare.com
// @connect cppinsights.io
// @connect cdnjs.cloudflare.com
Expand Down Expand Up @@ -505,7 +506,7 @@ let RequestAPI = (Action, Data, CallBack) => {
}
GM_xmlhttpRequest({
method: "POST",
url: (UtilityEnabled("SuperDebug") ? "http://127.0.0.1:8787/" : "https://api.xmoj-bbs.me/") + Action,
url: (UtilityEnabled("SuperDebug") ? "http://127.0.0.1:8787/" : "https://api.xmoj-script.uk/") + Action,
headers: {
"Content-Type": "application/json",
"Cache-Control": "no-cache",
Expand Down Expand Up @@ -643,7 +644,7 @@ function ConnectNotificationSocket() {
return;
}

let wsUrl = (UtilityEnabled("SuperDebug") ? "ws://127.0.0.1:8787" : "wss://api.xmoj-bbs.me") + "/ws/notifications?SessionID=" + Session;
let wsUrl = (UtilityEnabled("SuperDebug") ? "ws://127.0.0.1:8787" : "wss://api.xmoj-script.uk") + "/ws/notifications?SessionID=" + Session;

if (UtilityEnabled("DebugMode")) {
console.log("WebSocket: Connecting to", wsUrl);
Expand Down Expand Up @@ -922,7 +923,7 @@ if (UtilityEnabled("AutoLogin") && document.querySelector("body > a:nth-child(1)
}

let SearchParams = new URLSearchParams(location.search);
let ServerURL = (UtilityEnabled("DebugMode") ? "https://ghpages.xmoj-bbs.me/" : "https://www.xmoj-bbs.me")
let ServerURL = (UtilityEnabled("DebugMode") ? "https://ghpages.xmoj-script.uk/" : "https://www.xmoj-script.uk")
if (document.querySelector("#profile") === null && !logined) {
location.href = "https://www.xmoj.tech/loginpage.php";
}
Expand Down Expand Up @@ -5026,7 +5027,7 @@ int main()
"Image": Reader.result
}, (ResponseData) => {
if (ResponseData.Success) {
Content.value = Before + `![](https://assets.xmoj-bbs.me/GetImage?ImageID=${ResponseData.Data.ImageID})` + After;
Content.value = Before + `![](https://assets.xmoj-script.uk/GetImage?ImageID=${ResponseData.Data.ImageID})` + After;
Content.dispatchEvent(new Event("input"));
} else {
Content.value = Before + `![上传失败!` + ResponseData.Message + `]()` + After;
Expand Down Expand Up @@ -5282,7 +5283,7 @@ int main()
"Image": Reader.result
}, (ResponseData) => {
if (ResponseData.Success) {
ContentElement.value = Before + `![](https://assets.xmoj-bbs.me/GetImage?ImageID=${ResponseData.Data.ImageID})` + After;
ContentElement.value = Before + `![](https://assets.xmoj-script.uk/GetImage?ImageID=${ResponseData.Data.ImageID})` + After;
ContentElement.dispatchEvent(new Event("input"));
} else {
ContentElement.value = Before + `![上传失败!]()` + After;
Expand Down Expand Up @@ -5455,7 +5456,7 @@ int main()
"Image": Reader.result
}, (ResponseData) => {
if (ResponseData.Success) {
ContentElement.value = Before + `![](https://assets.xmoj-bbs.me/GetImage?ImageID=${ResponseData.Data.ImageID})` + After;
ContentElement.value = Before + `![](https://assets.xmoj-script.uk/GetImage?ImageID=${ResponseData.Data.ImageID})` + After;
ContentElement.dispatchEvent(new Event("input"));
} else {
ContentElement.value = Before + `![上传失败!]()` + After;
Expand Down Expand Up @@ -5713,7 +5714,7 @@ int main()
"Image": Reader.result
}, (ResponseData) => {
if (ResponseData.Success) {
ContentEditor.value = Before + `![](https://assets.xmoj-bbs.me/GetImage?ImageID=${ResponseData.Data.ImageID})` + After;
ContentEditor.value = Before + `![](https://assets.xmoj-script.uk/GetImage?ImageID=${ResponseData.Data.ImageID})` + After;
ContentEditor.dispatchEvent(new Event("input"));
} else {
ContentEditor.value = Before + `![上传失败!]()` + After;
Expand Down
36 changes: 36 additions & 0 deletions child-protection.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>儿童保护协议 - 小明的OJ增强脚本</title>
<link rel="icon" href="favicon.ico">
<link href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.2.3/css/bootstrap.min.css" rel="stylesheet">
Comment thread
PythonSmall-Q marked this conversation as resolved.
</head>
<body>
<div class="container py-4">
<h1 class="mb-3">儿童保护协议</h1>
<p class="text-muted">生效日期:2026-04-24</p>

<h3>一、适用范围</h3>
<p>本协议适用于未满十四周岁儿童相关信息处理活动,以及监护人对儿童使用服务的管理与监督。</p>

<h3>二、监护人责任</h3>
<p>监护人应指导并监督儿童合理使用网络服务,避免泄露隐私信息,防止沉迷与不当互动。</p>

<h3>三、儿童信息保护</h3>
<p>我们坚持最小必要原则处理信息,不会在无正当理由情况下收集与服务无关的儿童个人信息。</p>

<h3>四、风险防护措施</h3>
<p>我们将持续优化内容展示、交互提醒和异常行为识别机制,降低儿童接触不适宜内容和网络风险的可能性。</p>

<h3>五、监护人权利</h3>
<p>监护人有权查阅、更正或删除儿童相关信息,并可通过反馈渠道提出限制处理、停止服务等申请。</p>

<h3>六、联系我们</h3>
<p>如您对儿童信息保护有疑问,请通过项目公开反馈渠道联系我们,我们会及时处理并反馈结果。</p>

<p class="mt-4"><a href="index.html">返回首页</a></p>
</div>
</body>
</html>
18 changes: 14 additions & 4 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -48,13 +48,14 @@
<a class="nav-link" href="#About">关于</a>
</li>
<li class="nav-item">
<a class="nav-link" href="messages.html">短消息在线看
<span class="badge bg-warning text-dark ms-1">Alpha</span>
</a>
<a class="nav-link" href="messages.html">短消息在线看</a>
</li>
<li class="nav-item">
<a class="nav-link" href="https://elxmoj.xmoj-bbs.me" target="_blank" rel="noopener noreferrer">ELXMOJ 客户端</a>
</li>
<li class="nav-item">
<a class="nav-link" href="https://sso.xmoj-bbs.me" target="_blank" rel="noopener noreferrer">统一认证中心</a>
</li>
</ul>
</div>
</div>
Expand Down Expand Up @@ -968,7 +969,16 @@ <h2 class="accordion-header">
</div>
</div>
</div>
<div class="container text-center mb-3">
<div>我们的网站在萌国备案啦!我们的备案号是:<a href="https://icp.gov.moe/?keyword=20240425" target="_blank" rel="noopener noreferrer">萌ICP备20240425号</a></div>
<div class="mt-2">
<a href="privacy.html">隐私协议</a>
|
<a href="terms.html">服务协议</a>
|
<a href="child-protection.html">儿童保护协议</a>
</div>
</div>
</body>
<center>我们的网站在萌国备案啦!我们的备案号是:<a href="https://icp.gov.moe/?keyword=20240425" target="_blank">萌ICP备20240425号</a> </center>
</html>
<script src="https://xmojscript1.statuspage.io/embed/script.js"></script>
117 changes: 114 additions & 3 deletions messages.html
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,9 @@ <h4 class="mb-1">登录</h4>
<li class="nav-item">
<button class="nav-link" data-tab="bookmarklet">书签登录(桌面端)</button>
</li>
<li class="nav-item">
<button class="nav-link" data-tab="sso-develop">统一认证登录(开发中)</button>
Comment thread
PythonSmall-Q marked this conversation as resolved.
</li>
</ul>

<!-- Tab: Manual (default) -->
Expand Down Expand Up @@ -216,6 +219,22 @@ <h6 class="card-title">第二步:在 XMOJ 上点击书签</h6>
</div>
</div>
</div>

<!-- Tab: SSO Login -->
<div id="tab-sso" style="display:none;">
<div class="card border-0 shadow-sm">
<div class="card-body">
<h6 class="card-title mb-2">统一认证中心登录</h6>
<p class="card-text small text-body-secondary mb-3">
点击下方按钮跳转至统一认证中心授权,授权成功后将自动返回并完成登录。
</p>
<button class="btn btn-primary w-100" id="btn-sso-login">前往统一认证中心授权</button>
<p class="small text-body-secondary mt-2 mb-0">
授权地址:sso.xmoj-bbs.me,回调地址:xmoj-bbs.me/messages.html
</p>
</div>
</div>
</div>
</div>

<!-- ==================== SCREEN: MAIL LIST ==================== -->
Expand Down Expand Up @@ -367,8 +386,8 @@ <h5 class="modal-title" id="user-info-modal-title">用户信息</h5>
'use strict';

// ── Constants ──────────────────────────────────────────────────────────────
const API_BASE = 'https://api.xmoj-bbs.me/';
const ASSET_BASE = 'https://assets.xmoj-bbs.me/GetImage?ImageID=';
const API_BASE = 'https://api.xmoj-script.uk/';
const ASSET_BASE = 'https://assets.xmoj-script.uk/GetImage?ImageID=';
const XMOJ_BASE = 'https://www.xmoj.tech';
const WEBUI_VERSION = 'webui-1.0.0';
const STORAGE_USER = 'xmoj-msg-username';
Expand All @@ -377,6 +396,11 @@ <h5 class="modal-title" id="user-info-modal-title">用户信息</h5>
const SCROLL_THRESHOLD = 80;
const MAX_IMAGE_BYTES = 5 * 1024 * 1024;
const PREVIEW_LEN = 60;
const OAUTH_AUTHORIZE_URL = 'https://sso.xmoj-bbs.me/authorize';
const OAUTH_CLIENT_ID = 'app_0f4cbbddfa42c0c4576cba8f7598d0a2';
const OAUTH_SCOPE = 'openid profile email xmoj_profile';
const OAUTH_REDIRECT_URI = 'https://xmoj-bbs.me/messages.html';
const STORAGE_OAUTH_STATE = 'xmoj-msg-oauth-state';

// ── State ──────────────────────────────────────────────────────────────────
let currentUser = null; // { username, phpsessid }
Expand Down Expand Up @@ -931,6 +955,7 @@ <h5 class="modal-title" id="user-info-modal-title">用户信息</h5>
});
document.getElementById('tab-bookmarklet').style.display = tab === 'bookmarklet' ? '' : 'none';
document.getElementById('tab-manual').style.display = tab === 'manual' ? '' : 'none';
document.getElementById('tab-sso').style.display = tab === 'sso' ? '' : 'none';
}

document.querySelectorAll('#loginTabs .nav-link').forEach(function(btn) {
Expand All @@ -939,6 +964,88 @@ <h5 class="modal-title" id="user-info-modal-title">用户信息</h5>
});
});

function generateUuidV4() {
if (window.crypto && window.crypto.randomUUID) return window.crypto.randomUUID();
if (window.crypto && window.crypto.getRandomValues) {
var bytes = new Uint8Array(16);
window.crypto.getRandomValues(bytes);
bytes[6] = (bytes[6] & 0x0f) | 0x40;
bytes[8] = (bytes[8] & 0x3f) | 0x80;
var hex = Array.from(bytes).map(function(b) { return b.toString(16).padStart(2, '0'); }).join('');
return [hex.slice(0, 8), hex.slice(8, 12), hex.slice(12, 16), hex.slice(16, 20), hex.slice(20)].join('-');
}
Comment thread
PythonSmall-Q marked this conversation as resolved.
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
var r = Math.random() * 16 | 0;
var v = c === 'x' ? r : (r & 0x3 | 0x8);
return v.toString(16);
});
Comment thread
PythonSmall-Q marked this conversation as resolved.
}

function startSsoLogin() {
var state = generateUuidV4() + '.' + generateUuidV4();
localStorage.setItem(STORAGE_OAUTH_STATE, state);
var params = new URLSearchParams({
response_type: 'code',
client_id: OAUTH_CLIENT_ID,
redirect_uri: OAUTH_REDIRECT_URI,
scope: OAUTH_SCOPE,
state: state
});
}

async function exchangeSsoCode(code, state) {
var res = await fetch(API_BASE + 'ExchangeSSOCode', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
Code: code,
State: state,
RedirectURI: OAUTH_REDIRECT_URI,
ClientID: OAUTH_CLIENT_ID
})
});
if (!res.ok) throw new Error('HTTP ' + res.status);
return res.json();
}

async function handleSsoCallback() {
var url = new URL(location.href);
var code = url.searchParams.get('code');
var state = url.searchParams.get('state');
if (!code) return false;

var expectedState = localStorage.getItem(STORAGE_OAUTH_STATE);
if (!state || !expectedState || state !== expectedState) {
showToast('统一认证登录失败:state 校验失败', 'danger');
url.searchParams.delete('code');
Comment thread
PythonSmall-Q marked this conversation as resolved.
url.searchParams.delete('state');
history.replaceState(null, '', url.pathname + (url.searchParams.toString() ? '?' + url.searchParams.toString() : ''));
return false;
}

try {
var result = await exchangeSsoCode(code, state);
if (!result || !result.Success || !result.Data) {
throw new Error((result && result.Message) || '后端未返回有效登录数据');
}
if (!result.Data.Username || !result.Data.SessionID) {
throw new Error('返回数据缺少 Username 或 SessionID');
}

localStorage.removeItem(STORAGE_OAUTH_STATE);
saveSession(result.Data.Username, result.Data.SessionID);
url.searchParams.delete('code');
url.searchParams.delete('state');
history.replaceState(null, '', url.pathname + (url.searchParams.toString() ? '?' + url.searchParams.toString() : ''));
showToast('统一认证登录成功', 'success');
onLoggedIn();
return true;
} catch (err) {
showToast('统一认证登录失败:' + err.message, 'danger');
Comment thread
PythonSmall-Q marked this conversation as resolved.
return false;
}
}

// ── Event Wiring ───────────────────────────────────────────────────────────
document.getElementById('btn-manual-login').addEventListener('click', function() {
var username = document.getElementById('input-username').value.trim();
Expand All @@ -948,6 +1055,8 @@ <h5 class="modal-title" id="user-info-modal-title">用户信息</h5>
onLoggedIn();
});

document.getElementById('btn-sso-login').addEventListener('click', startSsoLogin);

document.getElementById('btn-logout').addEventListener('click', logout);

document.getElementById('btn-refresh-list').addEventListener('click', loadMailList);
Expand Down Expand Up @@ -1034,7 +1143,7 @@ <h5 class="modal-title" id="user-info-modal-title">用户信息</h5>
});

// ── Boot ───────────────────────────────────────────────────────────────────
(function init() {
(async function init() {
initTheme();
initBookmarklet();

Expand All @@ -1050,6 +1159,8 @@ <h5 class="modal-title" id="user-info-modal-title">用户信息</h5>
return;
}

if (await handleSsoCallback()) return;

if (loadSession()) {
onLoggedIn();
} else {
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "xmoj-script",
"version": "3.4.5",
"version": "3.4.6",
"description": "an improvement script for xmoj.tech",
"main": "AddonScript.js",
"scripts": {
Expand Down
Loading
Loading