duangsuse::Echo
717 subscribers
4.26K photos
130 videos
583 files
6.48K links
import this:
美而不丑、明而不暗、短而不凡、长而不乱,扁平不宽,读而后码,行之天下,勿托地上天国。
异常勿吞,难过勿过,叹一真理。效率是很重要,盲目最是低效。
简明是可靠的先验,不是可靠的祭品。
知其变,守其恒,为天下式;穷其变,知不穷,得地上势。知变守恒却穷变知新,我认真理,我不认真。

技术相干订阅~
另外有 throws 闲杂频道 @dsuset
转载频道 @dsusep
极小可能会有批评zf的消息 如有不适可退出
suse小站(面向运气编程): https://WOJS.org/#/
Download Telegram
Forwarded from dnaugsuz
这名字有点长啊... luaInstance 难不成是单例的... 为什么不直接上 Kotlin 推荐的 object 单例(还可以 Mixin 接口啥的
State 的需求很常见啊,虚拟机的 LuaState 都上全局状态就缺少灵活性了

一些 trivial 的定义就直接划等号好了,如果返回值有差异也可以不严格遵循缩进,写到一行去,毕竟「一个行为」占多行也挺怪的,不像「语句块」(statement compound) 这种东西的本意

val func0: ((arg: CPointer<ByteVar>?) -> ...) 这个 arg 就不用写了,有点余赘

typealias char_p = CPointer<ByteVar>?

此外 regFunction 可以考虑泛化一下,如果有的话也该重命名为 regStr2StrFunction 之类的啊... 标识符的信息量有点容易模糊了,一些通用数据表述转换的模式也该抽提出来

val wrapCKs2Cs: (char_p) -> char_p = { strfunc(it!!.toKString()).cstr.getPointer(this) }

char_p (aka. CPointer<ByteVar>?) 上的 toKString 可以抽提一下,弄个 forceString() / mustString() 什么的
(反正是在 KN 侧编程,什么 KString 啊 CString 啊... 扩展方法 monkey patch 限制下作用域就好了)

这个 memScope 有点奇怪... 谁帮我解释一下 总感觉这个 wrapper 函数的 result 分配生存期似乎不应该是相对于注册它的函数来说的,而是相对于调用它的函数来说的,这部分的分配和收集本应该交给 LuaC_* Lua 侧的 GC 来完成,或许应该注册 userdata? 或者用 Lua 的字符串池创建接口?
不熟悉 KN 的 GC... 我也不知道它是 Tracing 的(Graph Walker)还是 Refcount 的还是二者兼有的... 只知道 KN 的 GC 实现还算小,大概不会是半空间/分代收集/碰撞分配的(要不然也不会有 NativeHeap , 指不定是空闲链表,Lua 也是空闲链表...)
Forwarded from 立音喵
KN 的自动释放内存区域
在里面创建的 getPointer 会在出这个范围时自动 free
Forwarded from dnaugsuz
emmm.... 可是如果先出作用域再有某作用域的分配呢? 会不会直接 free

闭包的算不算 memScoped 里面的... 或许算吧
Forwarded from dnaugsuz
怀疑这么写会导致空字符串指针
返回给 Lua 调用侧的看起来好像早就被标记 free 了

regFun(gname, sfunc) = memScoped {
val wrap = { it: char_p -> ...cstr.getPointer(this@memScoped) }
lua.regFun(gname, wrap)
}
Forwarded from 立音喵
是的…
Forwarded from 立音喵
所以现在已经不是这样了
#Telegram 不管怎么说,我觉得 SCP 的开发者们很有水平,包括是项目最开始『元设计』(把设计的缘由和要点,真正的需求写出来)、项目作者的公钥和联系方式、每个项目的详情(作为博文发布,且有一个包含大概需要的列表)

他们的架构设计也很有意思,虽然基本上完全是 Python 写的,但有趣的地方是: 他们利用了 Telegram Bot 和平台本身的集成度,把每个步骤/流程都解耦合划分给了单独的 Bot 实例,通过 boardcast message 来实现数据交流
这不仅仅意味着一切对管理者来说更加可见,还增加了机器人们的灵活性(比如,处理某种消息的机器人可以在某个 Telegram 频道和其他平台模块「对接」,从而在更大的范围里处理消息、群主可以随意引入需要的机器人同时节约计算资源、模块高内聚低耦合无须考虑内部实现、不必在一台主机上运作)

当然,在某些方面也有不好的地方(增加了计算资源开销,因为每个机器人都要遍历消息一遍,如果文本处理结果的复用没做好,开销会稍微大、机器人文本传输给 Telegram 增加了一点点负担、项目结构虽然易读模块化低耦合但略微臃肿)

也有一些我没法理解的地方,比如说 SCP-079 的 HIDE 机器人,其功能就是接收有发布消息广播功能机器人的信息转发请求,做一个『发布代理』(当然,只是二次转发,其出处可以是在一个私有群组/不知名广播里)
而这个代理存在的理由只是「帮助隐藏管理机器人的身份,免得 spammer 认出它们」
可是 「spammer」 就算是认出了机器人又能怎么办呢?
如果 spammer 认出机器人就不发垃圾消息,可如果机器人保证自己会检查每个人的消息并且提供群成员手工检查渠道,广告就不能存在,而且经常给机器人换马甲也比较麻烦。

HIDE 机器人的代码质量很高,文件划分得也很不错,但是隐隐约约也能感到它划分得有些过头了,因为 HIDE 使用的文件数目相对于它的功能 — 仅仅是按照某频道里的请求转发消息 实在是多得过分
channel.py 是拿来管理交换的,可其中和模块 HIDE 有相当大关系的只有 exchange_to_hide。 format_data 和 share_data 里有不少代码都是和这个模块本身完全无关的,本来应该多划分一个 SCP079-COMMON Python3 包,却采取了每个项目复制一遍的方式来管理
— 为了保证良好的异常安全性,几乎是所有的函数都加上了 try/except,即便有些函数根本不可能抛出异常(比如序列化用的 dumps)
— 同样是异常安全性:

from threading import Thread
from json import dumps

'''
Thread helper function
要我写会这么写
'''
def thread(routn: callable, argv: tuple, nam: str = None,
catcher: callable = lambda e: log.warn(f"Thread fail: {e}", exc_info=True), daem = False) -> Thread:
try:
thr = Thread(target=routn, args=argv)
thr.name, thr.daemon = (thr.name if nam is None else nam, daem)
thr.start()
return thr
except Exception as e:
catcher(e, thr)
return None

'''
那 SCP 写的时候一些辅助函数是怎么写的?
'''
def bold(text: Any) -> str:
# Get a bold text # 注释而不是文档
try:
text = str(text) # 强制类型转换
if text.strip(): # 尝试在不应该失败的调用上异常安全
return f"<b>{escape(str(text))}</b>" # 太「空」的缩进语义,应该用 return ... if 写在一行内
except Exception as e:
logger.warning(f"Bold error: {e}", exc_info=True) # 极端情况(比如内存耗尽)下,logger 也不一定可以正常工作
return "" # 控制流不直接(当然也没办法...)

def format_data(sender: str, receivers: List[str], action: str, action_type: str,
data: Union[bool, dict, int, str] = None) -> str:
....
except Exception as e: # 在不应该失败的情况下处理错误
logger.warning(f"Format data error: {e}", exc_info=True)

SCP 写的时候有点太过拘泥于某种特定的格式了,也没有用代码块(block) 来进一步抽提逻辑
总之,虽然 SCP 很注重模块化细分,但复用却做得不是很好(必要的模块居然被称为插件!),一个项目里居然能看到 7 个文件全是冗余可共享代码的情况...

附:HIDE 的文件
channel 数据交换管理,其中三个函数皆可复用而无必要放在特定模块里
etc 辅助函数(诸如文本格式化),其中绝大部分函数为无异常(noexcept)甚至纯函数,但全都被加上了 except Exception as e: logger.warn(f"{e}", exc_info = True)
还包含一个 thread 辅助函数和一个 flood_wait 函数,其中 flood_wait 为串行(serial) 阻塞线程的函数,且使用了不必要的 random.uniform [min, maxI), 应考虑将消息发布转为 async 函数来提升便利性和并发量
filters 包含 Pyogram 的两个 Filter(大概是 Telegram 支持机器人只处理特定消息,可这好像完全是在客户端处理的)
from_user, exchange_channel, hide_channel, test_group 其中一个冗余(在使用元编程的情况下四个均冗余),可用 1~2 个函数抽提逻辑
逻辑表达式(兼控制流断言)代码质量欠佳、在不可能失败的情况(0 分配布耳表达式)下依然 except:...
        if message.chat:
cid = message.chat.id # 代码质量不佳
if glovar.should_hide:
if cid == glovar.hide_channel_id:
return True
elif cid == glovar.exchange_channel_id:
return True
should be:
xchannel_id = glovar.hide_channel_id if glovar.should_hide else glovar.exchange_channel_id
is_exchange_channel = ptgfilt(lambda m: m.chat && m.chat.id == xchannel_id)

receive 也是两个可共享逻辑
telegram 的 forward_messages 属于可以共享的范畴,可以考虑抽提。 send_message 属于应该共享的子程序
其中,Telegram 的 FloodWait 可以利用函数装饰器抽提
不可能出错的函数不应该异常安全处理 try: / if text.strip():
对 None 的判断应该显式标记 if text.strip():
在 Python 里,使用提前返回而不是嵌套 if
对于单个 Flag 的 while 循环,使用 break / next 比 flood_wait = True... 要强
如果可以不用 ret 系变量仅仅用于临时存储返回值,就不用或者最小化限制其作用域 result = None ... return result,这里 result 可以更名为 lastresult 以强化重试到成功的逻辑

backup: 简单的 ping 逻辑,完全可以在所有 bot 间共享,但却每个 bot 复制了一份

文档里经常出现这样的代码实例:

exchange = format_data(sender = 'LONG', receivers = ['MANAGE'], action_type = 'request', action = 'leave', data = /groupReason/)

也恰恰证明了这是一个协作有方的团体,尽管并不大。
包括 Telegram 上明确的文件列表和叙述、清楚的模块细分,也是高水平程序员的标杆
Forwarded from dnaugsuz
讲个笑话:文本上直接中文分词 + Naive bayes 朴素贝叶丝分类(如果 Chinese Lexical Analyize 全字典大了就上精简字典什么的... 有并行计算高性能框架的)
短图二维码和 short link 跳转用消息队列慢慢查

行为上算个大体的策略,虽然不得不接收所有消息的 onMessage, 可是对可信用户的检查可直接跳过 / 随机抽查 / 添加人工 bot command 允许群友重新审查
可信用户的选择可以对每个群设置一个基准,连续发送 N 个安全消息的就算可信用户

行为不要老是看维度什么的,又不必要 kNN(何况 ML 领域又不是 kNN 一家独大,kNN 明明是最入门的算法之一,虽然也不只是 kNN 可以接收「多维度」),你反个 spam 比 spammer 还开销,还不如不反
Telegram 的 spammer 一般也就靠二维码、short link、裸消息文字了,二维码的还不能发重复图片,因为会被直接认出来

很多 userbot spammer 就是加能加的群,然后入群慢慢发广告,发几个可能退出可能继续待着
普通用户就算是偶尔刷消息,在广告屏蔽上也不应该予以屏蔽,也可以节省资源来做到更大并发量

非得从 0 开始监督式学习,要大量数据,反而不好,何况这个模型也不好设计(即便我们只是说「用户的行为」,比如说 Telegram 里有些群只在中午交流、有些群是深夜,得为每个群制定标准,这合适吗?)

我觉得没必要为了 anti-spam 去收集用户行为,因为对几乎所有群聊来说,很多 spammer 就是发个广告而已,可以做简单一点,踢出屏蔽随机进攻的 AD userbot 即可

对于误封,可信用户若是不小心被查到(可能是误查)spam,则不踢出群聊,发警告 + restrict 就可以了,等待管理员确认
自从 @ANTI_SCP_079 事件发生以来,作为项目的发起者,我从未公开发声。一部分原因是,我不想助长恶意捣乱者的兴致,给他们的关注越多,他们就越高兴,而我们说得越多,他们断章取义得就越多;另一部分原因是,我在等待一个草稿原则以及方案的完成,此草稿一定程度上说明了 SCP-079 的初心与思路。

当然这中间有很多人劝我应该说点什么。是的,当有人无端指责我们的时候,我应该说点什么的,当有人利用网络暴力人肉出我们的开发者的时候,我应该说点什么的,当有人聚集起来抵制抹黑所有中文反广告机器人的时候,我应该说点什么的。

但,我怎么说呢?

我该向别人建议的那样心平气和地向所有人做汇报吗?我该像木头人一样对待这种事情丝毫不生气吗?我该一板一眼地念几句公关文字来回应吗?不,不,不,我不想也变得像质疑我们项目的人那样,质疑他们背后的真正目的,我也不想陷入和他们争论的无止境循环,今天我只想说一些肺腑之言。

有人有这种论调:那你们什么额外目的也没有,为了什么?用爱发电吗?你们总得图点什么吧。

对此我想回答:是的,我们就是用爱发电。我不想说 “如果你们这样下去,会把真正做事的开发者的心弄凉的” 这样的话,会有人认为这是威胁论。反而,我想说,正如反对机器人的人士说的那样,“一个机器人倒下去,千千万万个机器人站起来”,即使你把一个开发者的心弄凉了,还有另一个开发者、还有另一群开发者有 “用爱发电” 的心。

所以,即使之前的草稿方案可能经过我们的后续讨论会完全推倒,但我还是选择这么早就把草稿放出来,为的是分享我们的想法,虽然并不成熟、并未实现、可能变化,但是所有人都能看到我们的想法,有心人或批判、或继承,都可以利用其中的一些想法去实现自己的项目。这样假使 SCP-079 项目组倒下了,也会有人去做类似的事情。

抵制中文防广告机器人的人士,你们要明白一点:只要群组仍旧受广告侵袭一天,需要自动化处理中文广告的需求就会存在一天。这点是不会以任何人的意志为转移的。你们当然有自由今天抵制张三,明天抵制李四,但你们得意识到,必须有人做类似的防广告项目。所以充分考虑到你们的透明需求,我们在一开始就选择要开源,如果不信任我们托管的公共服务,任何人都可以根据源码动手搭建自己的服务。我们同样提供完善的搭建指南,确保有一定动手能力的人都可以顺利完成自行搭建的任务。

不过遗憾的是,原方案的主要开发者已经由于抵制 SCP-079 人士的人肉,被迫退出了项目组。我们原本有些起色的进度,陷入了停滞状态,即使其他工作繁忙的开发者能抽出时间接手,我们还要重新考虑原方案,乃至从头规划一个新方案。从我个人角度来看,我不得不说,这是很令人受挫的事情,@ANTI_SCP_079,你把我们的一片好心给糟蹋了。

如果很乐观地讲,SCP-079 项目最终能够完成。我们也会对申请使用机器人的群组进行认真的审核,确保真正负责任的、有意愿的群组去使用它,而不是那些只是盲目地为了图省事的群组。群主及管理在享受机器人带来的便利、节省大量时间的同时,也应该有一定付出,首先自己要花时间去了解机器人运作的机制,并做到和群友之间的良好沟通,让他们知道我们群有什么机器人,如果对封禁有疑问该找谁,今天删了我的表情包是因为什么,明天我发一段俄语被删了又是因为什么——说清楚自己群组做的自定义设置,而不是一味地只推说是什么机器人删除的。如果反广告机器人本身都得不到群主的理解、支持和配合,那么面对一些无中生有的指责,机器人也只能百口莫辨。好的生态需要双方的共同努力,我想这个观点对大部分机器人都适用。

以上就是我个人想说的话。下面我介绍一下原则草稿和方案草稿,事先声明:此草稿并不能作为最终我们采用的方案,并且其中的错误是在所难免的,我们也不希望任何人使用草稿中的纰露之处来继续对项目进行攻击。

— TEAM SCP-079 [origin]
#JavaScript ... 这个问题其实蛮没价值转发到这里的,提个醒。
Forwarded from dnaugsuz
不可以 eval(const ....)
Forwarded from dnaugsuz
... 其实 const 不应该用作 eval 的字符串,有点这个意思
即使它可以定义变量为不可变
Forwarded from dnaugsuz
eval 是有值的,所以你本来可以

const config = eval(env.CONFIG_JS);


或者

const config = fileA_exists? eval(faExpr) : eval(fbExpr);

最好不要滥用动态
Forwarded from dnaugsuz
let [a, b] = [1, 2];
const c = eval('a + b');
//c=3
Forwarded from dnaugsuz
在不必要的情况下使用 eval 是坏习惯
如果不是要动态生成 JavaScript 代码或者要实现插件系统之类的东西,不要用 evalFunction(String)
不能把可以写死的代码写进 eval
比如能够

global['str'] = 1;
就不要在非严格模式下(此时变量是顶层对象的属性)
eval(`str = 1`);
#China #Life 🤔 从某种意义上不错
Forwarded from 无羽の碎碎念
总觉得Scheduled Message有一个很可怕的使用方式:
留遗言
Dijkstra.kt
4 KB
#Kotlin #Algorithm 本苏又双花了两个小时写了 Dijkstra 算法,发现自己的手速还是 Naive, foreach (cost, neibor) if (newcost < rs[neibor].cost) rs[neibor] = (newcost, step) 都写不出了
个人感觉质量较上次有很大提升,也未作过多无意义的逻辑结构抽提
还是本苏最爱的瞎 🐔B 「泛化」编程