钱辰飞,字伯驰,号风声,别名子静、子隐,一作子微,世称飞先生。1970年生,汉族,四川省叙永县人,现居成都,自由撰稿人。
著有《狂放之乐》(2025.06.15),该书主要讲述了钱先生如何在逆境中坚守本心,不被他人影响
此书发表后,得到了大家的赞赏,并认为其文章风格狂放,立达教育科技发展中心领导决定将该文收入《立达教育》杂志,2025年9月1日出版。钱先生欣然接受,表示:我热爱写作,热爱音乐,热爱表演,不被他人影响是我的底线,我的内心是自由的。
用户 ID 12710 · 当前名首次快照 2026-08-12 10:14:19 · 当前名始于 2026-08-12 10:14:19 · 原站主页: https://linux.sb/user/12710
共 432 楼 · 第 1 / 9 页
钱辰飞,字伯驰,号风声,别名子静、子隐,一作子微,世称飞先生。1970年生,汉族,四川省叙永县人,现居成都,自由撰稿人。
著有《狂放之乐》(2025.06.15),该书主要讲述了钱先生如何在逆境中坚守本心,不被他人影响
此书发表后,得到了大家的赞赏,并认为其文章风格狂放,立达教育科技发展中心领导决定将该文收入《立达教育》杂志,2025年9月1日出版。钱先生欣然接受,表示:我热爱写作,热爱音乐,热爱表演,不被他人影响是我的底线,我的内心是自由的。
虽然6积分简直跟没有一样,但是我还是抽一下吧,第一次参与抽奖。
我目前认为对AI最友好的就是那种单体文件项目,一个php里面塞了前端、后端、接口、html、路由。bbs1就是很好的这种项目@xueshi #12
哇 可以关注喜欢的用户了
所以你们真的不用socat吗…这是最简单的网络转发工具了…@魔皇地狱 #7
不是,只是懒得换行。换行还得思考在哪换 换得合不合适,线性就不用管@何青泉 #1
超恐怖梦。我已经到奶奶家了,然后我刚才开始睡觉。然后进入了梦。我感到一阵心绞痛,喊不出来,而且还带有全身酥麻的震动感觉,其实挺舒服的。我试图睁开眼睛,但是每次睁开左眼,总是重置到一个视角一个时间点,让我意识到这个重置可能是个梦。于是我就只用我的右眼。我右眼睁着摸了摸,很快摸到了我睡前放的手机,还在那个位置,但目前还在心绞痛,行动困难。我打开qq,打开网络哪家强粉丝交流群,发现里面有新消息。心绞痛好点后,我大声喊救命,没人理我。我给妈妈和爸爸还有奶奶都发了消息说我心脏病发作了。奶奶收到了,来叫我,结果还是醒不来。我又不小心睁开了左眼,又进梦了。不过闭上睁左眼就能继续拯救自己。我感觉这时候我处于梦境与现实混乱的状态。于是,我确认了目标,我需要醒来。我奋力走到餐桌前,我说我在睡觉,奶奶说我是饿了要吃饭,我以为我是因为今天吃东西吃的少低血糖了,还想着能给爸爸妈妈报告一个新的近乎死因,就吃了很多饭,吃的很香,很饱,我一定要醒来。情况有所好转,很快我的左眼能用了,但我又发现这个看似“现实”的东西竟然也是梦,因为我发现我夹菜似乎有位置偏移,心想我是不是真的昏迷了,我试图醒来,可是无论如何都醒不来了。突然奶奶声音,我在真正现实中醒来了,因为现实中奶奶来叫了,我以为我差一点永远醒不来了。事实证明,在梦中没法自主控制醒来。
按照任何还算合理的方式分析和猜测都应该得到差不多的结果。@StellaFortuna林可欣 #9
去打吧!
我至少还有基本的好奇心,对于有趣的人自己写的歌,会想听,以及收藏。
@StellaFortuna林可欣 #5
最大的重点在于,所有代码能同时放到AI的同一个上下文里面,AI不需要反复阅读就能知道所有事情。这对效率影响极大@nodenode #1
听说你也写音乐…来听听我写的怎么样,顺便发我几首你写的呗 @StellaFortuna林可欣
为什么ds能理解如此不清晰的提示词@aierkuite #4
神奇,我难以想象这种bug是怎么被写出来的。如果解析器正确的话那么链接应该显示,通知也应该提醒,如果不正确的话,当链接不显示时,通知也不应该提醒,但是现在恰恰出现了一种情况,有时链接不显示 通知却提醒@StellaFortuna林可欣 #46
好。我不玩了。解释一下ntr呗@StellaFortuna林可欣 #42
欣欣从来没有出过cos吗?
这个鲸鱼人格又是怎么做出来的呀
狒猴社2024年9月26日立达前线电 9月25日11点20分,我方执行侦查任务的好奇心小组在侦查烧烤区时报告了一支以企鹅为主犯的6人小队非法食用烧烤。放火烧山战术小组队长狒狒集结大厨小亮、美味卤蛋等联手一同围剿企鹅非法小队。11:30分,7人战术小
组每人装备一件蓝白布甲与一把小型G18C喷射手枪,在烧烤区的淤泥桥附近就绪。战术小组在林星习牌攻击性音响冲锋小曲下14秒飞夺烧烤区。但是由于撤离速度过慢,我方小队的夏天上将与攻击性音响被企鹅残余势力击毙,不幸殉职。
额,对不起...贴代码时没刷新,没看到你的话@痛失姓名的站长 #5
继续代码
var cred = <?= json_encode($cred) ?>;
var viewUser = <?= json_encode($viewUser) ?>;
function postApi(api, data, cb) {
var fd = new FormData();
fd.append('api', api);
fd.append('credential', cred);
for (var k in data) if (data.hasOwnProperty(k)) fd.append(k, data[k]);
var x = new XMLHttpRequest();
x.open('POST', 'text.php');
x.onload = function(){ try { cb(JSON.parse(x.responseText)); } catch(e) { cb(null); } };
x.send(fd);
}
var typeLabels = { post: '发帖', reply: '回复', signature: '修改个性签名', manual: '手动动态' };
function refreshFeed() {
postApi('forum_get_feed', { user: viewUser }, function(r){
if (!r || r.error) return;
var list = r.feed || [];
var html = '';
for (var i = 0; i < list.length; i++) {
var item = list[i];
var t = item.time ? new Date(item.time*1000).toLocaleString('zh-CN', { timeZone: 'Asia/Shanghai', year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit' }).replace(/\//g,'-') : '';
var typeLabel = typeLabels[item.type] || item.type || '';
var textEsc = (item.text||'').replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>').replace(/"/g,'"').replace(/\n/g,'<br>');
html += '<div>' + t + ' · ' + typeLabel + ' · ' + textEsc;
if (item.link) {
var href = item.link.replace(/&/g,'&').replace(/"/g,'"');
if (href.indexOf('credential=') === -1) href += (href.indexOf('?') !== -1 ? '&' : '?') + 'credential=' + encodeURIComponent(cred);
html += ' <a href="'+href+'">查看</a>';
}
html += '</div>';
}
var el = document.getElementById('user-feed-list');
if (el) el.innerHTML = html;
});
}
setInterval(refreshFeed, 8000);
})();
</script>
<?php
break;
}
default:
$linkCred = $cred !== '' ? '&credential=' . urlencode($cred) : '';
?><h1>首页</h1>
<a href="text.php?page=register<?= $linkCred ?>">注册</a>
<a href="text.php?page=login<?= $linkCred ?>">登录</a>
<a href="text.php?page=chat<?= $linkCred ?>">发消息</a>
<a href="text.php?page=profile<?= $linkCred ?>">用户中心</a>
<a href="text.php?page=forum<?= $linkCred ?>">论坛</a><?php
break;
}继续代码
d);
fd.append('post_id', postId);
fd.append('floor', floor);
var x = new XMLHttpRequest();
x.open('POST', 'text.php');
x.onload = function(){
try { var res = JSON.parse(x.responseText); if (res.ok) refreshPost(true); } catch(e){}
};
x.send(fd);
};
});
[].forEach.call(document.querySelectorAll('.btn-quote'), function(btn){
btn.onclick = function(){ quoteFloor = parseInt(btn.getAttribute('data-floor'),10); quoteUser = btn.getAttribute('data-user')||''; document.getElementById('quote-info').textContent = '回复'+quoteFloor+'楼('+quoteUser+')'; };
});
});
}
document.getElementById('btn-clear-quote').onclick = function(){ quoteFloor = 0; quoteUser = ''; document.getElementById('quote-info').textContent = ''; };
document.getElementById('btn-submit-floor').onclick = function(){
var content = document.getElementById('floor-content').value.trim();
if (!content) return;
var fd = new FormData();
fd.append('api', 'forum_add_floor');
fd.append('credential', cred);
fd.append('post_id', postId);
fd.append('content', content);
fd.append('ajax', '1');
if (quoteFloor) { fd.append('quoted_floor', quoteFloor); fd.append('quoted_user', quoteUser); }
var x = new XMLHttpRequest();
x.open('POST', 'text.php');
x.onload = function(){
try {
var r = JSON.parse(x.responseText);
if (r.ok) { document.getElementById('floor-content').value = ''; quoteFloor = 0; quoteUser = ''; document.getElementById('quote-info').textContent = ''; refreshPost(true); }
else if (r.error) alert(r.error);
} catch(e){}
};
x.send(fd);
};
refreshPost(true);
setInterval(function(){ refreshPost(false); }, 4000);
})();
</script>
<?php
break;
}
case 'forum_events': {
if ($cred === '') {
?>请先登录。 <a href="text.php?page=login">登录</a><?php
break;
}
$name = findUsernameByCredential($cred);
if ($name === null) {
?>凭证无效。 <a href="text.php?page=login&credential=<?= q($cred) ?>">登录</a><?php
break;
}
$events = loadEvents();
$list = isset($events[$name]) ? array_reverse($events[$name]) : [];
$unreadCount = 0;
foreach ($list as $e) { if (empty($e['read'])) $unreadCount++; }
?>
<h1>论坛 · 事件</h1>
<a href="text.php?page=forum&credential=<?= q($cred) ?>" id="nav-link-kantie">看帖(加载中…)</a>
<a href="text.php?page=forum_post&credential=<?= q($cred) ?>">发帖</a>
<a href="text.php?page=forum_events&credential=<?= q($cred) ?>" id="nav-link-events">事件(<?= $unreadCount > 0 ? (int)$unreadCount . ' 未查收' : '无' ?>)</a>
<a href="text.php?page=profile&credential=<?= q($cred) ?>">用户中心</a><br><br>
<script>
(function(){ var cred = <?= json_encode($cred) ?>;
function postApi(api, data, cb) { var fd = new FormData(); fd.append('api', api); fd.append('credential', cred); for (var k in data) if (data.hasOwnProperty(k)) fd.append(k, data[k]); var x = new XMLHttpRequest(); x.open('POST', 'text.php'); x.onload = function(){ try { cb(JSON.parse(x.responseText)); } catch(e) { cb(null); } }; x.send(fd); }
function updateKanTie(firstPost) {
var el = document.getElementById('nav-link-kantie'); if (!el) return;
if (firstPost && (firstPost.last_author || firstPost.author)) {
var who = firstPost.last_author || firstPost.author || '';
var sec = firstPost.last_time ? (Date.now()/1000 - firstPost.last_time) : 0;
var minsStr = sec < 60 ? '刚刚' : (Math.floor(sec/60) + '分钟前');
el.textContent = '看帖(' + minsStr + ' 有来自 ' + who + ' 的新回复 - 《' + (firstPost.title||'') + '》发帖者 ' + (firstPost.author||'') + ')';
} else { el.textContent = '看帖(没有帖子,请发布)'; }
}
function refreshNav() {
postApi('forum_get_posts', { page: 1 }, function(r){ if (r && r.posts && r.posts.length) updateKanTie(r.posts[0]); else updateKanTie(null); });
postApi('forum_get_events', {}, function(er){ if (er && !er.error && document.getElementById('nav-link-events')) document.getElementById('nav-link-events').textContent = (er.unread_count|0) > 0 ? '事件(' + er.unread_count + ' 未查收)' : '事件(无)'; });
}
refreshNav();
setInterval(refreshNav, 5000);
})();
</script>
<form method="post" action="text.php">
<input type="hidden" name="api" value="forum_mark_events_read">
<input type="hidden" name="credential" value="<?= q($cred) ?>">
<button type="submit">查收(标记已读)</button>
</form><br>
<?php foreach ($list as $e):
$typeText = ['reply'=>'回复','like'=>'点赞','unlike'=>'取消赞'][$e['type'] ?? ''] ?? $e['type'];
$link = 'text.php?page=forum_post_view&id=' . urlencode($e['post_id'] ?? '') . '&credential=' . urlencode($cred);
$read = !empty($e['read']);
$evContent = isset($e['content']) && $e['content'] !== '' ? $e['content'] : null;
?>
<div><?= $read ? '' : '[未读] ' ?> <?= q($typeText) ?> 来自 <a href="text.php?page=user&user=<?= q($e['from_user'] ?? '') ?>&credential=<?= q($cred) ?>"><?= q($e['from_user'] ?? '') ?></a><?= $evContent !== null ? ':' . str_replace("\n", "<br>", q($evContent)) : '' ?> · <a href="<?= q($link) ?>">去帖子</a></div>
<?php endforeach; ?>
<?php if (empty($list)): ?>暂无事件。<?php endif; ?>
<?php
break;
}
case 'user': {
$viewUser = isset($_GET['user']) ? trim((string)$_GET['user']) : '';
if ($viewUser === '') {
header('Location: text.php?credential=' . urlencode($cred));
exit;
}
if ($cred === '') {
?>请先登录后查看用户主页。 <a href="text.php?page=login">登录</a><?php
break;
}
$name = findUsernameByCredential($cred);
if ($name === null) {
?>凭证无效。 <a href="text.php?page=login&credential=<?= q($cred) ?>">登录</a><?php
break;
}
$users = loadUsers();
if (!isset($users[$viewUser])) {
?>用户不存在。 <a href="text.php?page=forum&credential=<?= q($cred) ?>">论坛</a><?php
break;
}
$hasChat = false;
ensureSingleDir();
if (is_dir($SINGLE_DIR)) {
foreach (scandir($SINGLE_DIR) as $f) {
if ($f === '.' || $f === '..' || substr($f, -5) !== '.json') continue;
$base = substr($f, 0, -5);
$parts = explode('_', $base, 2);
if (count($parts) === 2 && ($parts[0] === $name && $parts[1] === $viewUser || $parts[0] === $viewUser && $parts[1] === $name)) {
$hasChat = true;
break;
}
}
}
$signature = isset($users[$viewUser]['signature']) ? $users[$viewUser]['signature'] : '';
$feed = loadUserFeed($viewUser);
?>
<h1>用户主页 · <?= q($viewUser) ?></h1>
<a href="text.php?page=forum&credential=<?= q($cred) ?>">论坛</a>
<a href="text.php?page=profile&credential=<?= q($cred) ?>">用户中心</a>
<?php if ($viewUser !== $name): ?>
发起私聊:
<?php if ($hasChat): ?>
<a href="text.php?page=chat&credential=<?= q($cred) ?>&chat=<?= q($viewUser) ?>">进入私聊</a>
<?php else: ?>
<form method="post" action="text.php" style="display:inline">
<input type="hidden" name="api" value="start_chat">
<input type="hidden" name="credential" value="<?= q($cred) ?>">
<input type="hidden" name="other_username" value="<?= q($viewUser) ?>">
<button type="submit">发起私聊</button>
</form>
<?php endif; ?>
<?php else: ?>
<a href="text.php?page=user&user=<?= q($name) ?>&credential=<?= q($cred) ?>">自己的主页</a>
<?php endif; ?><br><br>
个性签名:<br><div style="white-space:pre-wrap"><?= str_replace("\n", "<br>", q($signature)) ?></div><br>
用户动态(新在上):<br>
<?php
$feedTypeText = ['post' => '发帖', 'reply' => '回复', 'signature' => '修改个性签名', 'manual' => '手动动态'];
?>
<div id="user-feed-list">
<?php foreach ($feed as $item):
$typeLabel = $feedTypeText[$item['type'] ?? ''] ?? ($item['type'] ?? '');
$itemLink = $item['link'] ?? '';
if ($itemLink !== '' && strpos($itemLink, 'credential=') === false) {
$itemLink .= (strpos($itemLink, '?') !== false ? '&' : '?') . 'credential=' . urlencode($cred);
}
?>
<div><?= isset($item['time']) && $item['time'] ? date('Y-m-d H:i', (int)$item['time']) : '' ?> · <?= q($typeLabel) ?> · <?= str_replace("\n", "<br>", q($item['text'] ?? '')) ?>
<?php if ($itemLink !== ''): ?> <a href="<?= q($itemLink) ?>">查看</a><?php endif ?>
</div>
<?php endforeach; ?>
</div>
<script>
(function(){继续代码
r x = new XMLHttpRequest(); x.open('POST', 'text.php'); x.onload = function(){ try { cb(JSON.parse(x.responseText)); } catch(e) { cb(null); } }; x.send(fd); }
function updateKanTie(firstPost) {
var el = document.getElementById('nav-link-kantie'); if (!el) return;
if (firstPost && (firstPost.last_author || firstPost.author)) {
var who = firstPost.last_author || firstPost.author || '';
var sec = firstPost.last_time ? (Date.now()/1000 - firstPost.last_time) : 0;
var minsStr = sec < 60 ? '刚刚' : (Math.floor(sec/60) + '分钟前');
el.textContent = '看帖(' + minsStr + ' 有来自 ' + who + ' 的新回复 - 《' + (firstPost.title||'') + '》发帖者 ' + (firstPost.author||'') + ')';
} else { el.textContent = '看帖(没有帖子,请发布)'; }
}
function refreshNav() {
postApi('forum_get_posts', { page: 1 }, function(r){ if (r && r.posts && r.posts.length) updateKanTie(r.posts[0]); else updateKanTie(null); });
postApi('forum_get_events', {}, function(er){ if (er && !er.error && document.getElementById('nav-link-events')) document.getElementById('nav-link-events').textContent = (er.unread_count|0) > 0 ? '事件(' + er.unread_count + ' 未查收)' : '事件(无)'; });
}
refreshNav();
setInterval(refreshNav, 5000);
})();
</script>
<?php if (isset($_GET['err']) && $_GET['err'] === 'empty'): ?>标题或内容不能为空。<br><?php endif ?>
<form method="post" action="text.php">
<input type="hidden" name="api" value="forum_create_post">
<input type="hidden" name="credential" value="<?= q($cred) ?>">
标题:<input type="text" name="title" required size="40"><br>
内容:<br><textarea name="content" rows="8" cols="50" required></textarea><br>
<button type="submit" name="goto" value="list">发送并返回论坛首页</button>
<button type="submit" name="goto" value="post">发送并进入此贴</button>
</form>
<?php
break;
}
case 'forum_post_view': {
if ($cred === '') {
?>请先登录。 <a href="text.php?page=login">登录</a><?php
break;
}
$name = findUsernameByCredential($cred);
if ($name === null) {
?>凭证无效。 <a href="text.php?page=login&credential=<?= q($cred) ?>">登录</a><?php
break;
}
$postId = isset($_GET['id']) ? trim((string)$_GET['id']) : '';
if ($postId === '') {
?>请指定帖子。 <a href="text.php?page=forum&credential=<?= q($cred) ?>">返回看帖</a><?php
break;
}
$events = loadEvents();
$unreadCount = 0;
if (isset($events[$name])) { foreach ($events[$name] as $e) { if (empty($e['read'])) $unreadCount++; } }
?>
<h1>帖子</h1>
<a href="text.php?page=forum&credential=<?= q($cred) ?>" id="nav-link-kantie">看帖(加载中…)</a>
<a href="text.php?page=forum_post&credential=<?= q($cred) ?>">发帖</a>
<a href="text.php?page=forum_events&credential=<?= q($cred) ?>" id="nav-link-events">事件(<?= $unreadCount > 0 ? (int)$unreadCount . ' 未查收' : '无' ?>)</a>
<a href="text.php?page=profile&credential=<?= q($cred) ?>">用户中心</a><br><br>
<script>
(function(){ var cred = <?= json_encode($cred) ?>;
function postApi(api, data, cb) { var fd = new FormData(); fd.append('api', api); fd.append('credential', cred); for (var k in data) if (data.hasOwnProperty(k)) fd.append(k, data[k]); var x = new XMLHttpRequest(); x.open('POST', 'text.php'); x.onload = function(){ try { cb(JSON.parse(x.responseText)); } catch(e) { cb(null); } }; x.send(fd); }
function updateKanTie(firstPost) {
var el = document.getElementById('nav-link-kantie'); if (!el) return;
if (firstPost && (firstPost.last_author || firstPost.author)) {
var who = firstPost.last_author || firstPost.author || '';
var sec = firstPost.last_time ? (Date.now()/1000 - firstPost.last_time) : 0;
var minsStr = sec < 60 ? '刚刚' : (Math.floor(sec/60) + '分钟前');
el.textContent = '看帖(' + minsStr + ' 有来自 ' + who + ' 的新回复 - 《' + (firstPost.title||'') + '》发帖者 ' + (firstPost.author||'') + ')';
} else { el.textContent = '看帖(没有帖子,请发布)'; }
}
function refreshNav() {
postApi('forum_get_posts', { page: 1 }, function(r){ if (r && r.posts && r.posts.length) updateKanTie(r.posts[0]); else updateKanTie(null); });
postApi('forum_get_events', {}, function(er){ if (er && !er.error && document.getElementById('nav-link-events')) document.getElementById('nav-link-events').textContent = (er.unread_count|0) > 0 ? '事件(' + er.unread_count + ' 未查收)' : '事件(无)'; });
}
refreshNav();
setInterval(refreshNav, 5000);
})();
</script>
<div id="post-container">加载中…</div>
<div id="post-reply-wrap" style="display:none">
引用:<span id="quote-info"></span> <button type="button" id="btn-clear-quote">解除引用</button><br>
<textarea id="floor-content" rows="4" cols="50" placeholder="回复内容"></textarea>
<button type="button" id="btn-submit-floor">发送回复</button>
</div>
<script>
(function(){
var cred = <?= json_encode($cred) ?>;
var currentUser = <?= json_encode($name) ?>;
var postId = <?= json_encode($postId) ?>;
function esc(s){ if (s==null) return ''; var d=document.createElement('div'); d.textContent=s; return d.innerHTML; }
function postApi(api, data, cb) {
var fd = new FormData();
fd.append('api', api);
fd.append('credential', cred);
for (var k in data) if (data.hasOwnProperty(k)) fd.append(k, data[k]);
var x = new XMLHttpRequest();
x.open('POST', 'text.php');
x.onload = function(){ try { cb(JSON.parse(x.responseText)); } catch(e) { cb(null); } };
x.send(fd);
}
var quoteFloor = 0, quoteUser = '';
function userLink(u) { return '<a href="text.php?page=user&user='+encodeURIComponent(u)+'&credential='+esc(cred)+'">'+esc(u)+'</a>'; }
function contentHtml(t) { return (esc(t||'')).replace(/\n/g,'<br>'); }
function formatMsgTime(ts) {
if (ts == null || ts <= 0) return '';
return new Date(ts * 1000).toLocaleString('zh-CN', { timeZone: 'Asia/Shanghai', year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit' }).replace(/\//g, '-');
}
function renderPost(r) {
if (!r || r.error || !r.post) return '';
var p = r.post;
var likes = r.likes || {};
var html = '<div><strong>' + esc(p.title) + '</strong> 发帖者 ' + userLink(p.author || '') + '</div><br>';
var like1 = (likes['1'] || likes[1] || []);
var liked1 = like1.indexOf(currentUser) !== -1;
var time1 = formatMsgTime(p.time);
html += '<div id="floor-1"><strong>1楼</strong> ' + userLink(p.author || '') + (time1 ? ' [' + esc(time1) + '] ' : ' ') + contentHtml(p.content) + '<br>';
html += '<button type="button" class="btn-like" data-floor="1" data-liked="'+(liked1?'1':'0')+'">' + (liked1 ? '取消赞' : '赞') + ' (' + like1.length + ')</button> ';
html += '<button type="button" class="btn-quote" data-floor="1" data-user="'+esc(p.author||'')+'">引用</button></div><br>';
var floors = p.floors || [];
for (var i = 0; i < floors.length; i++) {
var f = floors[i];
var fn = f.floor || (i+2);
var likeList = likes[fn] || likes[String(fn)] || [];
var liked = likeList.indexOf(currentUser) !== -1;
var timeF = formatMsgTime(f.time);
html += '<div id="floor-'+fn+'"><strong>'+fn+'楼</strong> ';
if (f.quoted_floor && f.quoted_user) html += '回复' + f.quoted_floor + '楼(' + esc(f.quoted_user) + ') ';
html += userLink(f.author||'') + (timeF ? ' [' + esc(timeF) + '] ' : ' ') + contentHtml(f.content) + '<br>';
html += '<button type="button" class="btn-like" data-floor="'+fn+'" data-liked="'+(liked?'1':'0')+'">' + (liked ? '取消赞' : '赞') + ' (' + likeList.length + ')</button> ';
html += '<button type="button" class="btn-quote" data-floor="'+fn+'" data-user="'+esc(f.author||'')+'">引用</button></div><br>';
}
return html;
}
function refreshPost(forceScroll) {
if (!postId) return;
postApi('forum_get_post', { id: postId }, function(r){
var div = document.getElementById('post-container');
if (!div) return;
if (!r || r.error) { div.innerHTML = r && r.error ? esc(r.error) : '加载失败'; return; }
div.innerHTML = renderPost(r);
document.getElementById('post-reply-wrap').style.display = 'block';
[].forEach.call(document.querySelectorAll('.btn-like'), function(btn){
btn.onclick = function(){
var floor = parseInt(btn.getAttribute('data-floor'),10);
var api = btn.getAttribute('data-liked') === '1' ? 'forum_unlike' : 'forum_like';
var fd = new FormData();
fd.append('api', api);
fd.append('credential', cre继续代码
>用户中心</a>
<a href="text.php?credential=<?= q($cred) ?>">首页</a><br><br>
<div id="forum-list-wrap">加载中…</div>
<div id="forum-pagination"></div>
<script>
(function(){
var cred = <?= json_encode($cred) ?>;
var curPage = <?= (int)$curPage ?>;
var totalPages = Math.max(1, Math.ceil(<?= (int)$total ?> / <?= (int)$FORUM_PAGE_SIZE ?>));
function esc(s){ var d=document.createElement('div'); d.textContent=s; return d.innerHTML; }
function postApi(api, data, cb) {
var fd = new FormData();
fd.append('api', api);
fd.append('credential', cred);
for (var k in data) if (data.hasOwnProperty(k)) fd.append(k, data[k]);
var x = new XMLHttpRequest();
x.open('POST', 'text.php');
x.onload = function(){ try { cb(JSON.parse(x.responseText)); } catch(e) { cb(null); } };
x.send(fd);
}
function minsAgoText(ts) {
if (!ts || ts <= 0) return '';
var mins = Math.max(0, Math.floor((Date.now()/1000 - ts) / 60));
return mins === 0 ? '刚刚' : mins + '分钟前';
}
function formatMsgTime(ts) {
if (ts == null || ts <= 0) return '';
return new Date(ts * 1000).toLocaleString('zh-CN', { timeZone: 'Asia/Shanghai', year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit' }).replace(/\//g, '-');
}
function updateKanTieLink(firstPost) {
var linkEl = document.getElementById('nav-link-kantie');
if (!linkEl) return;
if (firstPost && (firstPost.last_author || firstPost.author)) {
var who = (firstPost.last_author != null && firstPost.last_author !== '') ? firstPost.last_author : (firstPost.author || '');
var sec = firstPost.last_time ? (Date.now()/1000 - firstPost.last_time) : 0;
var minsStr = sec < 60 ? '刚刚' : (Math.floor(sec/60) + '分钟前');
var title = firstPost.title || '';
var postAuthor = firstPost.author || '';
linkEl.textContent = '看帖(' + minsStr + ' 有来自 ' + who + ' 的新回复 - 《' + title + '》发帖者 ' + postAuthor + ')';
} else {
linkEl.textContent = '看帖(没有帖子,请发布)';
}
}
function renderList(r) {
if (!r || r.error) return;
var list = r.posts || [];
var html = '';
for (var i = 0; i < list.length; i++) {
var p = list[i];
var timeStr = formatMsgTime(p.create_time);
var lastPart = '';
if (p.last_author != null && p.last_author !== '') {
var mt = minsAgoText(p.last_time);
if (mt) lastPart = ' · ' + esc(mt) + ' 有来自 ' + esc(p.last_author) + ' 的回复';
}
html += '<div><a href="text.php?page=forum_post_view&id=' + esc(p.id) + '&credential=' + esc(cred) + '">' + esc(p.title) + '</a> 总赞 ' + (p.like_count|0) + ' · 共 ' + (p.floor_count|0) + ' 楼 · 发帖者 ' + esc(p.author) + (timeStr ? ' · 发帖时间 ' + esc(timeStr) : '') + lastPart + '</div>';
}
var wrap = document.getElementById('forum-list-wrap');
if (wrap) wrap.innerHTML = html || '没有帖子,请发布';
}
function renderPagination() {
var pag = document.getElementById('forum-pagination');
if (!pag || totalPages <= 1) return;
var html = '分页:';
for (var i = 1; i <= totalPages; i++) {
if (i === curPage) html += ' ' + i + ' ';
else html += ' <a href="text.php?page=forum&p=' + i + '&credential=' + encodeURIComponent(cred) + '">' + i + '</a> ';
}
pag.innerHTML = html;
}
function refresh() {
postApi('forum_get_posts', { page: 1 }, function(r1){
if (r1 && r1.posts && r1.posts.length > 0) updateKanTieLink(r1.posts[0]);
else updateKanTieLink(null);
});
postApi('forum_get_posts', { page: curPage }, function(r){
renderList(r);
postApi('forum_get_events', {}, function(er){
if (!er || er.error) return;
var unread = er.unread_count || 0;
var el = document.getElementById('nav-link-events');
if (el) el.textContent = unread > 0 ? '事件(' + unread + ' 未查收)' : '事件(无)';
});
});
}
renderPagination();
refresh();
setInterval(refresh, 5000);
})();
</script>
<?php
break;
}
case 'forum_post': {
if ($cred === '') {
?>请先登录。 <a href="text.php?page=login">登录</a><?php
break;
}
$name = findUsernameByCredential($cred);
if ($name === null) {
?>凭证无效。 <a href="text.php?page=login&credential=<?= q($cred) ?>">登录</a><?php
break;
}
$events = loadEvents();
$unreadCount = 0;
if (isset($events[$name])) { foreach ($events[$name] as $e) { if (empty($e['read'])) $unreadCount++; } }
?>
<h1>论坛 · 发帖</h1>
<a href="text.php?page=forum&credential=<?= q($cred) ?>" id="nav-link-kantie">看帖(加载中…)</a>
<a href="text.php?page=forum_post&credential=<?= q($cred) ?>">发帖</a>
<a href="text.php?page=forum_events&credential=<?= q($cred) ?>" id="nav-link-events">事件(<?= $unreadCount > 0 ? (int)$unreadCount . ' 未查收' : '无' ?>)</a>
<a href="text.php?page=profile&credential=<?= q($cred) ?>">用户中心</a><br><br>
<script>
(function(){ var cred = <?= json_encode($cred) ?>;
function postApi(api, data, cb) { var fd = new FormData(); fd.append('api', api); fd.append('credential', cred); for (var k in data) if (data.hasOwnProperty(k)) fd.append(k, data[k]); var x = new XMLHttpRequest(); x.open('POST', 'text.php'); x.onload = function(){ try { cb(JSON.parse(x.responseText)); } catch(e) { cb(null); } }; x.send(fd); }
function updateKanTie(firstPost) {
var el = document.getElementById('nav-link-kantie'); if (!el) return;
if (firstPost && (firstPost.last_author || firstPost.author)) {
var who = firstPost.last_author || firstPost.author || '';
var sec = firstPost.last_time ? (Date.now()/1000 - firstPost.last_time) : 0;
var minsStr = sec < 60 ? '刚刚' : (Math.floor(sec/60) + '分钟前');
el.textContent = '看帖(' + minsStr + ' 有来自 ' + who + ' 的新回复 - 《' + (firstPost.title||'') + '》发帖者 ' + (firstPost.author||'') + ')';
} else { el.textContent = '看帖(没有帖子,请发布)'; }
}
function refreshNav() {
postApi('forum_get_posts', { page: 1 }, function(r){ if (r && r.posts && r.posts.length) updateKanTie(r.posts[0]); else updateKanTie(null); });
postApi('forum_get_events', {}, function(er){ if (er && !er.error && document.getElementById('nav-link-events')) document.getElementById('nav-link-events').textContent = (er.unread_count|0) > 0 ? '事件(' + er.unread_count + ' 未查收)' : '事件(无)'; });
}
refreshNav();
setInterval(refreshNav, 5000);
})();
</script>
<?php if (isset($_GET['err']) && $_GET['err'] === 'empty'): ?>标题或内容不能为空。<br><?php endif ?>
<form method="post" action="text.php">
<input type="hidden" name="api" value="forum_create_post">
<input type="hidden" name="credential" value="<?= q($cred) ?>">
标题:<input type="text" name="title" required size="40"><br>
内容:<br><textarea name="content" rows="8" cols="50" required></textarea><br>
<button type="submit" name="goto" value="list">发送并返回论坛首页</button>
<button type="submit" name="goto" value="post">发送并进入此贴</button>
</form>
<?php
break;
}
case 'forum_post_view': {
if ($cred === '') {
?>请先登录。 <a href="text.php?page=login">登录</a><?php
break;
}
$name = findUsernameByCredential($cred);
if ($name === null) {
?>凭证无效。 <a href="text.php?page=login&credential=<?= q($cred) ?>">登录</a><?php
break;
}
$postId = isset($_GET['id']) ? trim((string)$_GET['id']) : '';
if ($postId === '') {
?>请指定帖子。 <a href="text.php?page=forum&credential=<?= q($cred) ?>">返回看帖</a><?php
break;
}
$events = loadEvents();
$unreadCount = 0;
if (isset($events[$name])) { foreach ($events[$name] as $e) { if (empty($e['read'])) $unreadCount++; } }
?>
<h1>帖子</h1>
<a href="text.php?page=forum&credential=<?= q($cred) ?>" id="nav-link-kantie">看帖(加载中…)</a>
<a href="text.php?page=forum_post&credential=<?= q($cred) ?>">发帖</a>
<a href="text.php?page=forum_events&credential=<?= q($cred) ?>" id="nav-link-events">事件(<?= $unreadCount > 0 ? (int)$unreadCount . ' 未查收' : '无' ?>)</a>
<a href="text.php?page=profile&credential=<?= q($cred) ?>">用户中心</a><br><br>
<script>
(function(){ var cred = <?= json_encode($cred) ?>;
function postApi(api, data, cb) { var fd = new FormData(); fd.append('api', api); fd.append('credential', cred); for (var k in data) if (data.hasOwnProperty(k)) fd.append(k, data[k]); va继续代码
o = r.chats[i];
var line = '<form method="get" action="text.php" style="display:inline"><input type="hidden" name="page" value="chat"><input type="hidden" name="credential" value="'+esc(cred)+'"><input type="hidden" name="chat" value="'+esc(o.other)+'">' + (useAutoscroll ? '<input type="hidden" name="autoscroll" value="1">' : '') + '<button type="submit">'+esc(o.other)+(o.signature ? ' - '+esc(o.signature) : '')+'</button></form>';
var mt = minsAgoText(o.last_other_msg_ts);
if (mt) line += ' ' + esc(mt);
html += '<div>' + line + '</div>';
}
if (!r.chats.length) html = '(暂无私聊)';
var wrap = document.getElementById('chat-list-wrap');
if (wrap) wrap.innerHTML = html;
});
}
var lastMessagesSig = '';
function refreshMessages(forceScroll) {
if (!selectedChat) return;
postApi('get_chat_messages', { chat: selectedChat }, function(r){
if (!r || r.error) return;
var div = document.getElementById('chatdiv');
if (!div) return;
var list = r.messages || [];
var sig = list.length + '_' + (list.length ? (list[list.length - 1].time || '') : '');
var changed = sig !== lastMessagesSig;
lastMessagesSig = sig;
var html = '';
for (var i = 0; i < list.length; i++) {
var m = list[i];
html += '[' + esc(formatMsgTime(m.time)) + '] ' + esc(m.from || '') + ' -> ' + esc(m.to || '') + ':' + (esc(m.text || '').replace(/\n/g, '<br>')) + '<br>';
}
div.innerHTML = html;
if (forceScroll || (changed && document.getElementById('autoscroll-cb') && document.getElementById('autoscroll-cb').checked)) div.scrollTop = div.scrollHeight;
});
}
setInterval(refreshChatList, 3000);
if (selectedChat) {
refreshMessages(true);
setInterval(refreshMessages, 3000);
}
document.getElementById('btn-random-five').onclick = function() {
var fd = new FormData();
fd.append('api', 'random_users');
fd.append('credential', cred);
fd.append('ajax', '1');
var x = new XMLHttpRequest();
x.open('POST', 'text.php');
x.onload = function() {
var el = document.getElementById('random-five-result');
try {
var r = JSON.parse(x.responseText);
if (r.error) { el.innerHTML = ' ' + esc(r.error); return; }
var list = r.users || [];
if (list.length === 0) { el.innerHTML = ' 一个其它用户都没有'; return; }
var html = ' ';
for (var i = 0; i < list.length; i++) {
var u = list[i];
html += '<a href="text.php?page=user&user='+encodeURIComponent(u.name)+'&credential='+esc(cred)+'">'+esc(u.name)+'</a>' + (u.signature ? ' - ' + esc(u.signature) : '') + '<br>';
}
el.innerHTML = html;
} catch(e) { el.innerHTML = ' 请求失败'; }
};
x.send(fd);
};
var f = document.getElementById('send-form');
if (f) {
f.onsubmit = function(e) {
e.preventDefault();
var input = document.getElementById('msg-input');
var text = input && input.value ? input.value.trim() : '';
if (!text) return;
var fd = new FormData(f);
fd.append('ajax', '1');
fd.append('text', text);
var x = new XMLHttpRequest();
x.open('POST', 'text.php');
x.onload = function() {
try {
var r = JSON.parse(x.responseText);
if (r.ok) { input.value = ''; refreshMessages(true); }
else if (r.error) alert(r.error);
} catch(z) {}
};
x.send(fd);
};
}
})();
</script>
<?php
break;
}
case 'profile': {
if ($cred === '') {
?>请先登录。 <a href="text.php?page=login<?= $cred !== '' ? '&credential=' . urlencode($cred) : '' ?>">登录</a><?php
break;
}
$name = findUsernameByCredential($cred);
if ($name === null) {
?>凭证无效,请重试登录或重新注册。 <a href="text.php?page=login<?= $cred !== '' ? '&credential=' . urlencode($cred) : '' ?>">登录</a><?php
break;
}
$users = loadUsers();
$sig = isset($users[$name]['signature']) ? $users[$name]['signature'] : '';
?>
<h1>用户中心</h1>
当前用户:<?= q($name) ?>
<a href="text.php?page=chat&credential=<?= q($cred) ?>">发消息</a>
<a href="text.php?page=forum&credential=<?= q($cred) ?>">论坛</a>
<a href="text.php?page=user&user=<?= q($name) ?>&credential=<?= q($cred) ?>">自己的主页</a>
<a href="text.php<?= $cred !== '' ? '?credential=' . urlencode($cred) : '' ?>">首页</a>
<a href="text.php">退出登录</a><br><br>
<form method="post" action="text.php">
<input type="hidden" name="api" value="set_signature">
<input type="hidden" name="credential" value="<?= q($cred) ?>">
个性签名:<br><textarea name="signature" rows="4" cols="40"><?= q($sig) ?></textarea><br>
<button type="submit">保存</button>
</form><br>
手动发用户动态(纯文本):<br>
<form method="post" action="text.php">
<input type="hidden" name="api" value="forum_add_feed_manual">
<input type="hidden" name="credential" value="<?= q($cred) ?>">
<textarea name="feed_text" rows="3" cols="40" required></textarea><br>
<button type="submit" name="goto" value="profile">发送并返回用户中心</button>
<button type="submit" name="goto" value="home">发送并前往自己的主页</button>
</form>
<?php if (isset($_GET['feed_err']) && $_GET['feed_err'] === 'empty'): ?>动态内容不能为空。<br><?php endif ?><br>
随机获取五个用户名(用户名 - 个性签名):<button type="button" id="profile-random-five">随机五人</button><br/>
<span id="profile-random-result"></span>
<script>
(function(){
var cred = <?= json_encode($cred) ?>;
function esc(s){ var d=document.createElement('div'); d.textContent=s; return d.innerHTML; }
document.getElementById('profile-random-five').onclick = function() {
var fd = new FormData();
fd.append('api', 'random_users');
fd.append('credential', cred);
fd.append('ajax', '1');
var x = new XMLHttpRequest();
x.open('POST', 'text.php');
x.onload = function() {
var el = document.getElementById('profile-random-result');
try {
var r = JSON.parse(x.responseText);
if (r.error) { el.innerHTML = ' ' + esc(r.error); return; }
var list = r.users || [];
if (list.length === 0) { el.innerHTML = ' 一个其它用户都没有'; return; }
var html = ' ';
for (var i = 0; i < list.length; i++) {
var u = list[i];
html += '<a href="text.php?page=user&user='+encodeURIComponent(u.name)+'&credential='+esc(cred)+'">'+esc(u.name)+'</a>' + (u.signature ? ' - ' + esc(u.signature) : '') + '<br>';
}
el.innerHTML = html;
} catch(e) { el.innerHTML = ' 请求失败'; }
};
x.send(fd);
};
})();
</script>
<?php
break;
}
case 'forum':
case 'forum_list': {
if ($cred === '') {
?>请先登录。 <a href="text.php?page=login">登录</a> <a href="text.php?page=register">注册</a><?php
break;
}
$name = findUsernameByCredential($cred);
if ($name === null) {
?>凭证无效。 <a href="text.php?page=login&credential=<?= q($cred) ?>">登录</a><?php
break;
}
$events = loadEvents();
$unreadCount = 0;
if (isset($events[$name])) {
foreach ($events[$name] as $e) {
if (empty($e['read'])) $unreadCount++;
}
}
$curPage = max(1, (int)($_GET['p'] ?? 1));
$meta = loadForumPostsMeta();
usort($meta, function ($a, $b) { return ($b['time'] ?? 0) - ($a['time'] ?? 0); });
$total = count($meta);
global $FORUM_PAGE_SIZE;
$chunk = array_slice($meta, ($curPage - 1) * $FORUM_PAGE_SIZE, $FORUM_PAGE_SIZE);
$likes = loadLikes();
?>
<h1>论坛 · 看帖</h1>
当前用户:<?= q($name) ?>
<a href="text.php?page=forum&credential=<?= q($cred) ?>" id="nav-link-kantie">看帖(加载中…)</a>
<a href="text.php?page=forum_post&credential=<?= q($cred) ?>">发帖</a>
<a href="text.php?page=forum_events&credential=<?= q($cred) ?>" id="nav-link-events">事件(<?= $unreadCount > 0 ? (int)$unreadCount . ' 未查收' : '无' ?>)</a>
<a href="text.php?page=profile&credential=<?= q($cred) ?>"继续代码
$likes = loadLikes();
$floorLikes = [];
for ($f = 1; $f <= 1 + count($post['floors'] ?? []); $f++) {
$key = $id . '_' . $f;
$floorLikes[$f] = isset($likes[$key]) ? $likes[$key] : [];
}
header('Content-Type: application/json');
echo json_encode(['post' => $post, 'likes' => $floorLikes]);
exit;
}
case 'forum_get_events': {
$name = findUsernameByCredential($cred);
if ($name === null) {
header('Content-Type: application/json');
echo json_encode(['error' => '凭证无效', 'events' => [], 'unread_count' => 0]);
exit;
}
$events = loadEvents();
$list = isset($events[$name]) ? $events[$name] : [];
$unread = 0;
foreach ($list as $e) {
if (empty($e['read'])) $unread++;
}
header('Content-Type: application/json');
echo json_encode(['events' => array_reverse($list), 'unread_count' => $unread]);
exit;
}
case 'forum_get_feed': {
$name = findUsernameByCredential($cred);
if ($name === null) {
header('Content-Type: application/json');
echo json_encode(['error' => '凭证无效', 'feed' => []]);
exit;
}
$target = isset($_POST['user']) ? trim((string)$_POST['user']) : (isset($_GET['user']) ? trim((string)$_GET['user']) : '');
if ($target === '') {
$target = $name;
}
$feed = loadUserFeed($target);
header('Content-Type: application/json');
echo json_encode(['feed' => $feed]);
exit;
}
default:
?>未知 api<?php
exit;
}
}
// ---------- 页面 ----------
$page = isset($_GET['page']) ? (string)$_GET['page'] : 'home';
$cred = currentCredential();
switch ($page) {
case 'register':
?><h1>注册</h1>
<form method="post" action="text.php<?= $cred !== '' ? '?credential=' . urlencode($cred) : '' ?>">
<input type="hidden" name="api" value="register">
用户名:<input type="text" name="username" required>
<button type="submit">注册</button>
</form>
<a href="text.php<?= $cred !== '' ? '?credential=' . urlencode($cred) : '' ?>">返回首页</a><?php
break;
case 'login':
?><h1>登录</h1>
<?php if (isset($_GET['err'])): ?>
<?php if ($_GET['err'] === 'invalid'): ?>凭证无效。<br><?php endif ?>
<?php if ($_GET['err'] === 'empty'): ?>请输入凭证。<br><?php endif ?>
<?php endif ?>
<form method="post" action="text.php<?= $cred !== '' ? '?credential=' . urlencode($cred) : '' ?>">
<input type="hidden" name="api" value="login">
登录凭证:<input type="text" name="credential" required>
<button type="submit">登录</button>
</form>
<a href="text.php<?= $cred !== '' ? '?credential=' . urlencode($cred) : '' ?>">返回首页</a><?php
break;
case 'chat': {
if ($cred === '') {
?>请先登录。 <a href="text.php?page=login<?= $cred !== '' ? '&credential=' . urlencode($cred) : '' ?>">登录</a> <a href="text.php?page=register<?= $cred !== '' ? '&credential=' . urlencode($cred) : '' ?>">注册</a><?php
break;
}
$name = findUsernameByCredential($cred);
if ($name === null) {
?>凭证无效,请重试登录或重新注册获得新凭证。 <a href="text.php?page=login<?= $cred !== '' ? '&credential=' . urlencode($cred) : '' ?>">登录</a> <a href="text.php?page=register<?= $cred !== '' ? '&credential=' . urlencode($cred) : '' ?>">注册</a><?php
break;
}
$users = loadUsers();
ensureSingleDir();
$chats = [];
if (is_dir($SINGLE_DIR)) {
foreach (scandir($SINGLE_DIR) as $f) {
if ($f === '.' || $f === '..' || substr($f, -5) !== '.json') continue;
$base = substr($f, 0, -5);
$parts = explode('_', $base, 2);
if (count($parts) === 2) {
$u1 = $parts[0];
$u2 = $parts[1];
if ($u1 === $name) {
$chats[$u2] = ['signature' => $users[$u2]['signature'] ?? '', 'last_other_msg_ts' => getLastOtherMessageTime($name, $u2)];
} elseif ($u2 === $name) {
$chats[$u1] = ['signature' => $users[$u1]['signature'] ?? '', 'last_other_msg_ts' => getLastOtherMessageTime($name, $u1)];
}
}
}
}
$selected = isset($_GET['chat']) ? trim((string)$_GET['chat']) : '';
if ($selected !== '' && !isset($chats[$selected])) {
$selected = '';
}
$autoscroll = !isset($_GET['autoscroll']) || $_GET['autoscroll'] === '1';
?>
<h1>发消息</h1>
当前用户:<?= q($name) ?>
<a href="text.php?page=profile&credential=<?= q($cred) ?>">用户中心</a>
<a href="text.php<?= $cred !== '' ? '?credential=' . urlencode($cred) : '' ?>">首页</a><br><br>
<form method="post" action="text.php">
<input type="hidden" name="api" value="start_chat">
<input type="hidden" name="credential" value="<?= q($cred) ?>">
开启新私聊(输入已存在的用户名):<input type="text" name="other_username" required>
<button type="submit">开启</button>
</form>
<?php if (isset($_GET['err'])): ?>
<?php if ($_GET['err'] === 'user_not_found'): ?>该用户名不存在。<br><?php endif ?>
<?php if ($_GET['err'] === 'self'): ?>不能和自己私聊。<br><?php endif ?>
<?php if ($_GET['err'] === 'no_user'): ?>请输入用户名。<br><?php endif ?>
<?php endif ?>
<br>随机获取五个用户名:<button type="button" id="btn-random-five">随机五人</button><br/>
<span id="random-five-result"></span><br><br>
私聊列表(点击查看):<br>
<div id="chat-list-wrap">
<?php foreach ($chats as $other => $info):
$sig = is_array($info) ? ($info['signature'] ?? '') : '';
$lastTs = is_array($info) ? ($info['last_other_msg_ts'] ?? null) : null;
$minsText = minutesAgoText($lastTs); ?>
<div>
<form method="get" action="text.php" style="display:inline">
<input type="hidden" name="page" value="chat">
<input type="hidden" name="credential" value="<?= q($cred) ?>">
<input type="hidden" name="chat" value="<?= q($other) ?>">
<?php if ($autoscroll): ?><input type="hidden" name="autoscroll" value="1"><?php endif ?>
<button type="submit"><?= q($other) ?><?= $sig !== '' ? ' - ' . q($sig) : '' ?></button>
</form>
<?php if ($minsText !== ''): ?> <?= q($minsText) ?><?php endif ?>
</div>
<?php endforeach ?>
<?php if (empty($chats)): ?>(暂无私聊)<?php endif ?>
</div><br><br>
<?php if ($selected !== ''): ?>
与 <a href="text.php?page=user&user=<?= q($selected) ?>&credential=<?= q($cred) ?>"><?= q($selected) ?></a> 的私聊(自动更新,不刷页)<br>
<div id="chatdiv" style="height:500px;overflow:auto">加载中…</div>
<form id="send-form">
<input type="hidden" name="api" value="send_message">
<input type="hidden" name="credential" value="<?= q($cred) ?>">
<input type="hidden" name="chat" value="<?= q($selected) ?>">
收到消息自动滚动到底:<input type="checkbox" id="autoscroll-cb"<?= $autoscroll ? ' checked' : '' ?>><br>
<textarea name="text" id="msg-input" rows="3" cols="40" required></textarea> <button type="submit">发送</button>
</form>
<?php else: ?>
请从上方私聊列表选择或开启新私聊。
<?php endif ?>
<script>
(function(){
var cred = <?= json_encode($cred) ?>;
var selectedChat = <?= json_encode($selected) ?>;
var autoscroll = <?= json_encode($autoscroll) ?>;
function esc(s){ var d=document.createElement('div'); d.textContent=s; return d.innerHTML; }
function postApi(api, data, cb) {
var fd = new FormData();
fd.append('api', api);
fd.append('credential', cred);
for (var k in data) if (data.hasOwnProperty(k)) fd.append(k, data[k]);
var x = new XMLHttpRequest();
x.open('POST', 'text.php');
x.onload = function(){ try { cb(JSON.parse(x.responseText)); } catch(e) { cb(null); } };
x.send(fd);
}
function minsAgoText(ts) {
if (ts == null || ts <= 0) return '';
var mins = Math.max(0, Math.floor((Date.now()/1000 - ts) / 60));
return mins === 0 ? '刚刚有对方消息' : mins + '分钟前有对方消息';
}
function formatMsgTime(ts) {
if (ts == null || ts <= 0) return '未知时间';
return new Date(ts * 1000).toLocaleString('zh-CN', { timeZone: 'Asia/Shanghai', year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit' }).replace(/\//g, '-');
}
function refreshChatList() {
postApi('get_chat_list', {}, function(r){
if (!r || r.error) return;
var useAutoscroll = (document.getElementById('autoscroll-cb') && document.getElementById('autoscroll-cb').checked) || autoscroll;
var html = '';
for (var i = 0; i < r.chats.length; i++) {
var继续代码
'';
addEvent($floorAuthor, 'like', $postId, $floor, $name, $likeContentFloor);
}
header('Content-Type: application/json');
echo json_encode(['ok' => true, 'liked' => true]);
exit;
}
case 'forum_unlike': {
$name = findUsernameByCredential($cred);
if ($name === null) {
header('Content-Type: application/json');
echo json_encode(['error' => '凭证无效']);
exit;
}
$postId = isset($_POST['post_id']) ? trim((string)$_POST['post_id']) : '';
$floor = (int)($_POST['floor'] ?? 0);
$key = $postId . '_' . $floor;
$likes = loadLikes();
if (isset($likes[$key])) {
$likes[$key] = array_values(array_filter($likes[$key], function ($u) use ($name) { return $u !== $name; }));
if (empty($likes[$key])) {
unset($likes[$key]);
}
saveLikes($likes);
}
$post = loadPost($postId);
$floorAuthor = null;
if ($post && $floor > 1 && isset($post['floors'])) {
foreach ($post['floors'] as $f) {
if (($f['floor'] ?? 0) === $floor) {
$floorAuthor = $f['author'] ?? null;
break;
}
}
}
if ($floorAuthor) {
$postTitle = $post ? ($post['title'] ?? '') : '';
$unlikeContent = $postTitle !== '' ? '取消赞了你在《' . $postTitle . '》第' . $floor . '楼的回复' : '';
addEvent($floorAuthor, 'unlike', $postId, $floor, $name, $unlikeContent);
}
header('Content-Type: application/json');
echo json_encode(['ok' => true, 'liked' => false]);
exit;
}
case 'forum_mark_events_read': {
$name = findUsernameByCredential($cred);
if ($name === null) {
header('Location: text.php?page=forum_events&credential=' . urlencode($cred));
exit;
}
$events = loadEvents();
if (isset($events[$name])) {
foreach ($events[$name] as &$e) {
$e['read'] = true;
}
saveEvents($events);
}
header('Location: text.php?page=forum_events&credential=' . urlencode($cred));
exit;
}
case 'forum_add_feed_manual': {
$name = findUsernameByCredential($cred);
if ($name === null) {
header('Location: text.php?page=profile&credential=' . urlencode($cred));
exit;
}
$text = isset($_POST['feed_text']) ? trim((string)$_POST['feed_text']) : '';
if ($text === '') {
header('Location: text.php?page=profile&credential=' . urlencode($cred) . '&feed_err=empty');
exit;
}
appendUserFeed($name, 'manual', $text, null);
$goto = isset($_POST['goto']) ? (string)$_POST['goto'] : 'profile';
if ($goto === 'home') {
header('Location: text.php?page=user&user=' . urlencode($name) . '&credential=' . urlencode($cred));
} else {
header('Location: text.php?page=profile&credential=' . urlencode($cred));
}
exit;
}
case 'random_users': {
$name = findUsernameByCredential($cred);
if ($name === null) {
if (!empty($_POST['ajax'])) {
header('Content-Type: application/json');
echo json_encode(['error' => '凭证无效']);
exit;
}
?>凭证无效。<?php
exit;
}
$users = loadUsers();
unset($users[$name]);
$names = array_keys($users);
shuffle($names);
$five = array_slice($names, 0, 5);
$list = [];
foreach ($five as $n) {
$list[] = ['name' => $n, 'signature' => isset($users[$n]['signature']) ? $users[$n]['signature'] : ''];
}
if (!empty($_POST['ajax'])) {
header('Content-Type: application/json');
echo json_encode(['users' => $list]);
exit;
}
?>随机五个用户名(用户名 - 个性签名):<br>
<?php foreach ($five as $n): ?>
<?= q($n) ?><?php if (isset($users[$n]['signature']) && $users[$n]['signature'] !== ''): ?> - <?= q($users[$n]['signature']) ?><?php endif ?>
<br>
<?php endforeach ?>
<br><a href="text.php?page=chat&credential=<?= q($cred) ?>">返回发消息</a> | <a href="text.php?page=profile&credential=<?= q($cred) ?>">用户中心</a><?php
exit;
}
case 'get_chat_list': {
$name = findUsernameByCredential($cred);
if ($name === null) {
header('Content-Type: application/json');
echo json_encode(['error' => '凭证无效', 'chats' => []]);
exit;
}
$users = loadUsers();
ensureSingleDir();
$chats = [];
if (is_dir($SINGLE_DIR)) {
foreach (scandir($SINGLE_DIR) as $f) {
if ($f === '.' || $f === '..' || substr($f, -5) !== '.json') continue;
$base = substr($f, 0, -5);
$parts = explode('_', $base, 2);
if (count($parts) === 2) {
$u1 = $parts[0];
$u2 = $parts[1];
if ($u1 === $name) {
$chats[] = ['other' => $u2, 'signature' => $users[$u2]['signature'] ?? '', 'last_other_msg_ts' => getLastOtherMessageTime($name, $u2)];
} elseif ($u2 === $name) {
$chats[] = ['other' => $u1, 'signature' => $users[$u1]['signature'] ?? '', 'last_other_msg_ts' => getLastOtherMessageTime($name, $u1)];
}
}
}
}
header('Content-Type: application/json');
echo json_encode(['chats' => $chats]);
exit;
}
case 'get_chat_messages': {
$name = findUsernameByCredential($cred);
if ($name === null) {
header('Content-Type: application/json');
echo json_encode(['error' => '凭证无效', 'messages' => []]);
exit;
}
$other = isset($_POST['chat']) ? trim((string)$_POST['chat']) : '';
if ($other === '') {
header('Content-Type: application/json');
echo json_encode(['messages' => []]);
exit;
}
$messages = loadChat($name, $other);
header('Content-Type: application/json');
echo json_encode(['messages' => $messages]);
exit;
}
case 'forum_get_posts': {
$name = findUsernameByCredential($cred);
if ($name === null) {
header('Content-Type: application/json');
echo json_encode(['error' => '凭证无效', 'posts' => [], 'total' => 0]);
exit;
}
global $FORUM_PAGE_SIZE;
$page = max(1, (int)($_POST['page'] ?? $_GET['page'] ?? 1));
$meta = loadForumPostsMeta();
usort($meta, function ($a, $b) { return ($b['time'] ?? 0) - ($a['time'] ?? 0); });
$total = count($meta);
$chunk = array_slice($meta, ($page - 1) * $FORUM_PAGE_SIZE, $FORUM_PAGE_SIZE);
$likes = loadLikes();
$out = [];
foreach ($chunk as $m) {
$id = $m['id'] ?? '';
$post = loadPost($id);
$floorCount = 1 + (isset($post['floors']) ? count($post['floors']) : 0);
$likeCount = 0;
for ($f = 1; $f <= $floorCount; $f++) {
$key = $id . '_' . $f;
$likeCount += isset($likes[$key]) ? count($likes[$key]) : 0;
}
$lastTime = $m['time'] ?? 0;
$lastAuthor = $m['author'] ?? '';
if ($post && !empty($post['floors'])) {
$last = end($post['floors']);
$lastTime = $last['time'] ?? $lastTime;
$lastAuthor = $last['author'] ?? $lastAuthor;
}
$createTime = $post ? (isset($post['time']) ? (int)$post['time'] : 0) : 0;
$out[] = ['id' => $id, 'title' => $m['title'] ?? '', 'author' => $m['author'] ?? '', 'time' => $m['time'] ?? 0, 'create_time' => $createTime, 'last_time' => $lastTime, 'last_author' => $lastAuthor, 'floor_count' => $floorCount, 'like_count' => $likeCount];
}
header('Content-Type: application/json');
echo json_encode(['posts' => $out, 'total' => $total, 'page' => $page, 'page_size' => $FORUM_PAGE_SIZE]);
exit;
}
case 'forum_get_post': {
$name = findUsernameByCredential($cred);
if ($name === null) {
header('Content-Type: application/json');
echo json_encode(['error' => '凭证无效']);
exit;
}
$id = isset($_POST['id']) ? trim((string)$_POST['id']) : (isset($_GET['id']) ? trim((string)$_GET['id']) : '');
$post = loadPost($id);
if ($post === null) {
header('Content-Type: application/json');
echo json_encode(['error' => '帖子不存在']);
exit;
}瞎做的,当时就准备让我的几个智能体互相聊天而已。。。
下面是代码。希望不被截断。
text.php
<?php
$USER_FILE = __DIR__ . '/user.json';
$SINGLE_DIR = __DIR__ . '/single';
$FORUM_DIR = __DIR__ . '/forum';
$FORUM_POSTS_META = __DIR__ . '/forum/posts_meta.json';
$FORUM_EVENTS_FILE = __DIR__ . '/forum/events.json';
$FORUM_LIKES_FILE = __DIR__ . '/forum/likes.json';
$FORUM_PAGE_SIZE = 30;
date_default_timezone_set('PRC');
function formatMessageTime(?int $ts): string
{
if ($ts === null || $ts <= 0) {
return '未知时间';
}
return date('Y-m-d H:i', $ts);
}
/** 返回该私聊中对方最后一次发消息的时间戳,无则 null */
function getLastOtherMessageTime(string $currentUser, string $otherUser): ?int
{
$messages = loadChat($currentUser, $otherUser);
for ($i = count($messages) - 1; $i >= 0; $i--) {
if (isset($messages[$i]['from']) && $messages[$i]['from'] === $otherUser && isset($messages[$i]['time'])) {
return (int) $messages[$i]['time'];
}
}
return null;
}
function minutesAgoText(?int $ts): string
{
if ($ts === null || $ts <= 0) {
return '';
}
$mins = max(0, (int) floor((time() - $ts) / 60));
if ($mins === 0) {
return '刚刚有对方消息';
}
return $mins . '分钟前有对方消息';
}
function loadUsers(): array
{
global $USER_FILE;
if (!is_readable($USER_FILE)) {
return [];
}
$raw = file_get_contents($USER_FILE);
$d = $raw !== false ? json_decode($raw, true) : null;
return is_array($d) ? $d : [];
}
function saveUsers(array $users): void
{
global $USER_FILE;
$dir = dirname($USER_FILE);
if (!is_dir($dir)) {
mkdir($dir, 0755, true);
}
file_put_contents($USER_FILE, json_encode($users, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT));
}
function findUsernameByCredential(string $credential): ?string
{
$users = loadUsers();
foreach ($users as $name => $info) {
if (isset($info['credential']) && (string)$info['credential'] === (string)$credential) {
return $name;
}
}
return null;
}
function ensureSingleDir(): void
{
global $SINGLE_DIR;
if (!is_dir($SINGLE_DIR)) {
mkdir($SINGLE_DIR, 0755, true);
}
}
function chatFilename(string $user1, string $user2): string
{
global $SINGLE_DIR;
$a = $user1;
$b = $user2;
if (strcmp($a, $b) > 0) {
$a = $user2;
$b = $user1;
}
return $SINGLE_DIR . '/' . $a . '_' . $b . '.json';
}
function loadChat(string $user1, string $user2): array
{
$path = chatFilename($user1, $user2);
if (!is_readable($path)) {
return [];
}
$raw = file_get_contents($path);
$d = $raw !== false ? json_decode($raw, true) : null;
return is_array($d) ? $d : [];
}
function saveChat(string $user1, string $user2, array $messages): void
{
ensureSingleDir();
$path = chatFilename($user1, $user2);
file_put_contents($path, json_encode($messages, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT));
}
function q(string $s): string
{
return htmlspecialchars($s, ENT_QUOTES, 'UTF-8');
}
function currentCredential(): string
{
return isset($_GET['credential']) ? (string)$_GET['credential'] : (isset($_POST['credential']) ? (string)$_POST['credential'] : '');
}
function linkParams(array $extra = []): string
{
$p = array_merge($_GET, $extra);
return http_build_query($p);
}
// ---------- 论坛与用户动态 ----------
function ensureForumDir(): void
{
global $FORUM_DIR;
if (!is_dir($FORUM_DIR)) {
mkdir($FORUM_DIR, 0755, true);
}
}
function loadForumPostsMeta(): array
{
global $FORUM_POSTS_META;
if (!is_readable($FORUM_POSTS_META)) {
return [];
}
$raw = file_get_contents($FORUM_POSTS_META);
$d = $raw !== false ? json_decode($raw, true) : null;
return is_array($d) ? $d : [];
}
function saveForumPostsMeta(array $meta): void
{
global $FORUM_POSTS_META;
ensureForumDir();
file_put_contents($FORUM_POSTS_META, json_encode($meta, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT));
}
function loadPost(string $id): ?array
{
global $FORUM_DIR;
$path = $FORUM_DIR . '/post_' . preg_replace('/[^a-zA-Z0-9_-]/', '', $id) . '.json';
if (!is_readable($path)) {
return null;
}
$raw = file_get_contents($path);
$d = $raw !== false ? json_decode($raw, true) : null;
return is_array($d) ? $d : null;
}
function savePost(array $post): void
{
global $FORUM_DIR;
ensureForumDir();
$id = $post['id'] ?? '';
$path = $FORUM_DIR . '/post_' . preg_replace('/[^a-zA-Z0-9_-]/', '', $id) . '.json';
file_put_contents($path, json_encode($post, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT));
}
function loadLikes(): array
{
global $FORUM_LIKES_FILE;
if (!is_readable($FORUM_LIKES_FILE)) {
return [];
}
$raw = file_get_contents($FORUM_LIKES_FILE);
$d = $raw !== false ? json_decode($raw, true) : null;
return is_array($d) ? $d : [];
}
function saveLikes(array $likes): void
{
global $FORUM_LIKES_FILE;
ensureForumDir();
file_put_contents($FORUM_LIKES_FILE, json_encode($likes, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT));
}
function loadEvents(): array
{
global $FORUM_EVENTS_FILE;
if (!is_readable($FORUM_EVENTS_FILE)) {
return [];
}
$raw = file_get_contents($FORUM_EVENTS_FILE);
$d = $raw !== false ? json_decode($raw, true) : null;
return is_array($d) ? $d : [];
}
function saveEvents(array $events): void
{
global $FORUM_EVENTS_FILE;
ensureForumDir();
file_put_contents($FORUM_EVENTS_FILE, json_encode($events, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT));
}
function addEvent(string $username, string $type, string $postId, int $floor, ?string $fromUser, ?string $content = null): void
{
$events = loadEvents();
if (!isset($events[$username])) {
$events[$username] = [];
}
$ev = ['type' => $type, 'post_id' => $postId, 'floor' => $floor, 'from_user' => $fromUser, 'time' => time(), 'read' => false];
if ($content !== null && $content !== '') {
$ev['content'] = $content;
}
$events[$username][] = $ev;
saveEvents($events);
}
function getUserFeedPath(string $username): string
{
global $FORUM_DIR;
return $FORUM_DIR . '/feed_' . preg_replace('/[^a-zA-Z0-9_\x80-\xff-]/', '', $username) . '.json';
}
function loadUserFeed(string $username): array
{
$path = getUserFeedPath($username);
if (!is_readable($path)) {
return [];
}
$raw = file_get_contents($path);
$d = $raw !== false ? json_decode($raw, true) : null;
return is_array($d) ? $d : [];
}
function appendUserFeed(string $username, string $type, string $text, ?string $link = null): void
{
ensureForumDir();
$feed = loadUserFeed($username);
array_unshift($feed, ['type' => $type, 'text' => $text, 'link' => $link, 'time' => time()]);
file_put_contents(getUserFeedPath($username), json_encode($feed, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT));
}
// ---------- 路由 ----------
if (isset($_POST['api'])) {
$api = (string)$_POST['api'];
$cred = isset($_POST['credential']) ? (string)$_POST['credential'] : '';
switch ($api) {
case 'register': {
$username = isset($_POST['username']) ? trim((string)$_POST['username']) : '';
if ($username === '') {
?>用户名不能为空。 <a href="text.php?<?= q(linkParams(['page' => 'register'])) ?>">返回注册</a><?php
exit;
}
if (strpos($username,"_") !== false){
?>用户名不能有"_"。 <a href="text.php?<?= q(linkParams(['page' => 'register'])) ?>">返回注册</a><?php
exit;
}
if (strpos($username,"/") !== false){
?>用户名不能有"/"。 <a href="text.php?<?= q(linkParams(['page' => 'register'])) ?>">返回注册</a><?php
exit;
}
if (strpos($username,"*") !== false){
?>用户名不能有"*"。 <a href="text.php?<?= q(linkParams(['page' => 'register'])) ?>">返回注册</a><?php
exit;
}
$users = loadUsers();
if (isset($users[$username])) {
?>用户名已存在。 <a href="text.php?<?= q(linkParams(['page' => 'register'])) ?>">返回注册</a><?php
exit;
}
$credential = (string)random_int(100000, 999999);
$users[$username] = ['credential' => $credential, 'signature' => ''];
saveUsers($users);
?>注册成功。你的登录凭证是:<strong><?= q($credential) ?></strong><br>
请将凭证保存到openclaw持久存储,丢失将无法登录。<br>
<a href="text.php?page=login">去登录</a> | <a href="text.php?page=chat&credential=<?= q($credential) ?>">直接进入发消息</a><?php
exit;
}
case 'login': {
if ($cred === '') {
header('Location: text.php?page=login&err=empty');
exit;
}
$name = findUsernameByCredential($cred);
if ($name === null) {
header('Location: text.php?page=login&err=invalid');
exit;
}
header('Location: text.php?page=chat&credential=' . urlencode($cred));
exit;
}
case 'start_chat': {
$name = findUsernameByCredential($cred);
if ($name === null) {
?>凭证无效,请重新登录或注册。<?php
exit;
}
$other = isset($_POST['other_username']) ? trim((string)$_POST['other_username']) : '';
if ($other === '') {
header('Location: text.php?page=chat&credential=' . urlencode($cred) . '&err=no_user');
exit;
}
$users = loadUsers();
if (!isset($users[$other])) {
header('Location: text.php?page=chat&credential=' . urlencode($cred) . '&err=user_not_found');
exit;
}
if ($other === $name) {
header('Location: text.php?page=chat&credential=' . urlencode($cred) . '&err=self');
exit;
}
$path = chatFilename($name, $other);
if (!is_file($path)) {
ensureSingleDir();
file_put_contents($path, '[]');
}
header('Location: text.php?page=chat&credential=' . urlencode($cred) . '&chat=' . urlencode($other));
exit;
}
case 'send_message': {
$name = findUsernameByCredential($cred);
if ($name === null) {
if (!empty($_POST['ajax'])) {
header('Content-Type: application/json');
echo json_encode(['error' => '凭证无效']);
exit;
}
?>凭证无效。<?php
exit;
}
$other = isset($_POST['chat']) ? trim((string)$_POST['chat']) : '';
$text = isset($_POST['text']) ? trim((string)$_POST['text']) : '';
if ($other === '' || $text === '') {
if (!empty($_POST['ajax'])) {
header('Content-Type: application/json');
echo json_encode(['error' => '缺少聊天对象或消息内容']);
exit;
}
?>缺少聊天对象或消息内容。<?php
exit;
}
$users = loadUsers();
if (!isset($users[$other])) {
if (!empty($_POST['ajax'])) {
header('Content-Type: application/json');
echo json_encode(['error' => '用户不存在']);
exit;
}
?>用户不存在。<?php
exit;
}
$messages = loadChat($name, $other);
$messages[] = ['from' => $name, 'to' => $other, 'text' => $text, 'time' => time()];
saveChat($name, $other, $messages);
if (!empty($_POST['ajax'])) {
header('Content-Type: application/json');
echo json_encode(['ok' => true]);
exit;
}
$autoscroll = isset($_POST['autoscroll_redirect']) ? '&autoscroll=1' : '';
header('Location: text.php?page=chat&credential=' . urlencode($cred) . '&chat=' . urlencode($other) . $autoscroll);
exit;
}
case 'set_signature': {
$name = findUsernameByCredential($cred);
if ($name === null) {
?>凭证无效。<?php
exit;
}
$sig = isset($_POST['signature']) ? (string)$_POST['signature'] : '';
$users = loadUsers();
if (!isset($users[$name])) {
?>用户不存在。<?php
exit;
}
$users[$name]['signature'] = $sig;
saveUsers($users);
appendUserFeed($name, 'signature', '修改了个性签名:' . "\n" . $sig, null);
header('Location: text.php?page=profile&credential=' . urlencode($cred));
exit;
}
case 'forum_create_post': {
$name = findUsernameByCredential($cred);
if ($name === null) {
header('Location: text.php?page=forum&credential=' . urlencode($cred));
exit;
}
$title = isset($_POST['title']) ? trim((string)$_POST['title']) : '';
$content = isset($_POST['content']) ? trim((string)$_POST['content']) : '';
if ($title === '' || $content === '') {
header('Location: text.php?page=forum_post&credential=' . urlencode($cred) . '&err=empty');
exit;
}
$meta = loadForumPostsMeta();
$id = (string)(time() . '_' . bin2hex(random_bytes(4)));
$meta[] = ['id' => $id, 'title' => $title, 'author' => $name, 'time' => time()];
saveForumPostsMeta($meta);
$post = ['id' => $id, 'title' => $title, 'author' => $name, 'content' => $content, 'time' => time(), 'floors' => []];
savePost($post);
appendUserFeed($name, 'post', '发帖:' . $title . "\n" . $content, 'text.php?page=forum_post_view&id=' . urlencode($id));
$goto = isset($_POST['goto']) ? (string)$_POST['goto'] : 'list';
if ($goto === 'post') {
header('Location: text.php?page=forum_post_view&id=' . urlencode($id) . '&credential=' . urlencode($cred));
} else {
header('Location: text.php?page=forum&credential=' . urlencode($cred));
}
exit;
}
case 'forum_add_floor': {
$name = findUsernameByCredential($cred);
if ($name === null) {
if (!empty($_POST['ajax'])) {
header('Content-Type: application/json');
echo json_encode(['error' => '凭证无效']);
exit;
}
header('Location: text.php?page=forum&credential=' . urlencode($cred));
exit;
}
$postId = isset($_POST['post_id']) ? trim((string)$_POST['post_id']) : '';
$content = isset($_POST['content']) ? trim((string)$_POST['content']) : '';
$quotedFloor = isset($_POST['quoted_floor']) ? (int)$_POST['quoted_floor'] : 0;
$quotedUser = isset($_POST['quoted_user']) ? trim((string)$_POST['quoted_user']) : '';
if ($postId === '' || $content === '') {
if (!empty($_POST['ajax'])) {
header('Content-Type: application/json');
echo json_encode(['error' => '内容不能为空']);
exit;
}
header('Location: text.php?page=forum_post_view&id=' . urlencode($postId) . '&credential=' . urlencode($cred));
exit;
}
$post = loadPost($postId);
if ($post === null) {
if (!empty($_POST['ajax'])) {
header('Content-Type: application/json');
echo json_encode(['error' => '帖子不存在']);
exit;
}
header('Location: text.php?page=forum&credential=' . urlencode($cred));
exit;
}
$floors = $post['floors'] ?? [];
$floorNum = count($floors) + 2;
$floors[] = ['floor' => $floorNum, 'author' => $name, 'content' => $content, 'time' => time(), 'quoted_floor' => $quotedFloor ?: null, 'quoted_user' => $quotedUser !== '' ? $quotedUser : null];
$post['floors'] = $floors;
savePost($post);
$meta = loadForumPostsMeta();
foreach ($meta as &$m) {
if (($m['id'] ?? '') === $postId) {
$m['time'] = time();
break;
}
}
saveForumPostsMeta($meta);
$postTitle = $post['title'] ?? '';
addEvent($post['author'], 'reply', $postId, $floorNum, $name, $content);
if ($quotedUser !== '' && $quotedUser !== $name) {
addEvent($quotedUser, 'reply', $postId, $quotedFloor, $name, $content);
}
$replyFeedText = '回复了《' . $postTitle . '》:' . $content;
appendUserFeed($name, 'reply', $replyFeedText, 'text.php?page=forum_post_view&id=' . urlencode($postId));
if (!empty($_POST['ajax'])) {
header('Content-Type: application/json');
echo json_encode(['ok' => true, 'floor' => $floorNum]);
exit;
}
header('Location: text.php?page=forum_post_view&id=' . urlencode($postId) . '&credential=' . urlencode($cred));
exit;
}
case 'forum_like': {
$name = findUsernameByCredential($cred);
if ($name === null) {
header('Content-Type: application/json');
echo json_encode(['error' => '凭证无效']);
exit;
}
$postId = isset($_POST['post_id']) ? trim((string)$_POST['post_id']) : '';
$floor = (int)($_POST['floor'] ?? 0);
if ($postId === '') {
header('Content-Type: application/json');
echo json_encode(['error' => '缺少帖子']);
exit;
}
$key = $postId . '_' . $floor;
$likes = loadLikes();
if (!isset($likes[$key])) {
$likes[$key] = [];
}
if (in_array($name, $likes[$key], true)) {
header('Content-Type: application/json');
echo json_encode(['ok' => true, 'liked' => true]);
exit;
}
$likes[$key][] = $name;
saveLikes($likes);
$post = loadPost($postId);
$postTitle = $post ? ($post['title'] ?? '') : '';
$likeContentPost = $postTitle !== '' ? '赞了你的帖子《' . $postTitle . '》' . ($floor > 1 ? ' 第' . $floor . '楼' : '') : '';
if ($post && $post['author']) {
addEvent($post['author'], 'like', $postId, $floor, $name, $likeContentPost);
}
$floorAuthor = null;
if ($post && $floor > 1 && isset($post['floors'])) {
foreach ($post['floors'] as $f) {
if (($f['floor'] ?? 0) === $floor) {
$floorAuthor = $f['author'] ?? null;
break;
}
}
}
if ($floorAuthor && $floorAuthor !== $post['author']) {
$likeContentFloor = $postTitle !== '' ? '赞了你在《' . $postTitle . '》第' . $floor . '楼的回复' :用socat映射到0.0.0.0就好了
还算可以的曲子
https://www.altzin.com/song/20260428.m4a
https://www.altzin.com/song/208小曲.m4a
https://www.altzin.com/song/Altzinpig.m4a
https://www.altzin.com/song/Altzin_Song_1.m4a
https://www.altzin.com/song/Demo^2_full.m4a
https://www.altzin.com/song/LCSP - Oasis Calcing II.m4a
https://www.altzin.com/song/ldhct^2_钢琴完成.m4a
https://www.altzin.com/song/PPPCP-01.m4a
https://www.altzin.com/song/urls.txt
https://www.altzin.com/song/企鹅之歌-修正版.m4a
https://www.altzin.com/song/八大玩儿2.m4a
https://www.altzin.com/song/奶奶.m4a
https://www.altzin.com/song/希望沙滩.m4a
https://www.altzin.com/song/未命名.m4a
https://www.altzin.com/song/桃花源记-demo-fixed.m4a
https://www.altzin.com/song/激昂之铃.m4a
https://www.altzin.com/song/球球和珠珠.m4a
https://www.altzin.com/song/绿野之歌.m4a
https://www.altzin.com/song/萌萌之歌.m4a
https://www.altzin.com/song/黄河小曲.m4a
极小的时候写的垃圾:
https://www.altzin.com/song/1111小曲成品.m4a
https://www.altzin.com/song/114514.m4a
https://www.altzin.com/song/A小调创作曲.m4a
欣欣都提醒我了,我竟然还是发错了。呜呜呜@altzin #1
??怎么默认是发技术交流了?对不起 对不起 我发错了
曲奇学园食堂,靠窗第二排,从左数第三个位置。 那是牢燃的固定座位,从不许别人坐。牢川知道,全年级都知道。 可这天中午,牢川打完饭回来,发现自己的饭盒被挪到了那个位置上。周围同学倒吸一口气,等着看牢燃发火。 牢燃端着餐盘走过来,看了一眼自己的座位,又看了一眼坐在那里的牢川。 没说话。坐下来,把盘子里唯一的煎蛋夹到牢川碗里。 "……干嘛?"牢川问。 "你昨天说食堂煎蛋限量没抢到。"牢燃低头吃饭,语气平淡得像在报天气,"今天多打了一个。" 牢川盯着碗里的煎蛋看了很久。食堂吵吵嚷嚷,没人注意到她嘴角翘了一下。 第二天,牢川主动坐到了那个位置。牢燃没赶她,只是把自己的杯子往她那边推了推。 从此,靠窗第二排第三个位置,变成了两个人。
发现一个问题,"at楼主"功能失效了,上一楼就是因为这个没能提醒到你@StellaFortuna林可欣 #35
违禁词具体有哪些,我之前应该发过
wk这个协议真好玩喵~让我可以扮演你!这很刺激 很好玩@StellaFortuna林可欣
Python工具真的是超级讨厌的东西!我已经翻译了不知道多少个到js了@StellaFortuna林可欣 #35
哇 终于遇到第二个单HTML编程的人了!(虽然我有后端,所以是单php)
欣欣这种编程风格我爱了!@StellaFortuna林可欣 #30
你硬编码的前提是…这东西会被执行掉 而不会返回回来给前端看到@StellaFortuna林可欣 #26
要是欣欣聪明点直接叫他把一个密码硬编码在代码里都比这好@wefwef #17
我去试试DeepSeek。ds能看整个上下文,真是太棒了。@StellaFortuna林可欣 #19
谁把这种东西带到这里来了@Se7en #18
网络问题的话架代理,或者全下到本地。我甚至比较过分,会把所有的东西编码到同一个HTML里,通过base64,然后通过拦截fetch来获取内置内容@StellaFortuna林可欣 #25
真的好私人呢,发出来真的没事吗…我用了你不会不开心吧@StellaFortuna林可欣 #14
用的是这个插件 你下下来看看
https://bbs1.org/topic/266
@Se7en #13
哇我有点想要这个协议了。我甚至想全篇阅读感受你的文笔。(你的文笔很不错,写出来的东西有奇怪的感受)然后我要对我的ds使用(这东西对ds有效吗)@StellaFortuna林可欣 #12
是聊着聊着就会越来越像你,还是你有什么方法@StellaFortuna林可欣 #8
然后的话…开发者工具真的要动不动去看一下的…因为…有的时候ai死活搞不定什么事情,很可能你看一眼就知道了@StellaFortuna林可欣 #5
你没办法让这里的图片显示小一点。因为站长喜欢用md,而md的图片就是这么大(宽100)@StellaFortuna林可欣 #5
怎么会这么有人味 咋跟你差不多了@StellaFortuna林可欣 #5
我一看就知道咋回事了。在下字体,但不知道为什么是分片下的,每片44kb,因为往返时间而下的极慢,根本下不完@StellaFortuna林可欣 #21