批量导出 QQ 空间说说

创作故事

  2015-03-16 为了导出自己的说说,写了个半自动的程序,手动分析几个参数填到代码理,很快就刷刷地下载了 7 年的说说。第二天,就在知乎回答了两贴。

如何一次性导出QQ空间说说?

如何批量导出特定 QQ 号的所有说说?

  2015-09-07 发现导出程序失效了,参数有点变化,但很快又跟进。

  2015-11-27 又失效了,除了 json 字段有变化,还增加了对 Cookie 的验证,于是又加上了 Cookie 的模拟。

  2015-12-11 又又失效了,这次增加了对 UserAgent 的验证……继续跟进。

  由于知乎的热度,越来越多的人找 UMU 导出,但这个程序是半自动的,会占用宝贵的时间,所以后来干脆让全职带孩子的脑波来,收点人工费。需要的可以联系 QQ:372769132(验证消息:qzone)。

收费规则

1. 8.88 元起,每 1000 条 8.88 元人民币;

2. 未满 1000 条部分,采用进一法,即 10001 条,是收费 8.88 * 2 = 17.76 元;

3. 封顶 88.88 元,所以,如果您的说说很多也不用害怕会很贵。

4. 支持 QQ 红包、微信、支付宝。

FAQ

1. 要交出 QQ 密码吗?

答:不必须。如果您的说说都是公开的,则完全没有必要交出密码。如果您发过只有自己或者少数好友可见的说说,则需要用您的账号密码登陆才能抓全。

2. 我的 QQ 空间被封了,别人都无法访问,可以导出吗?

答:只要您自己能访问就可以,但要提供您的账号和密码。您可以事先改一个临时密码,事后再改掉。

3. 导出的格式是什么样的?

答:主体是一个 json 文件,里面有您全部说说,包括说说本身、别人的回复、图片链接(没有图片本身)。另外生成一份 txt 文件,只有发帖时间和说说本身,其它都没有。

备注

如果有时间会改进,比如说搞成图文并茂的格式,也导出博客。

MongoDB Shard ID hash 算法 std::hash 的跨平台性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include <functional>
#include <iomanip>
#include <iostream>
#include <string>


int main()
{

std::string str = "Meet the new boss...";
std::size_t str_hash = std::hash<std::string>{}(str);
std::cout << "hash(" << std::quoted(str) << ") = " << str_hash << std::endl;

str = "Meet the new boss..;";
str_hash = std::hash<std::string>{}(str);
std::cout << "hash(" << std::quoted(str) << ") = " << str_hash << std::endl;

str = "Meet the new boss../";
str_hash = std::hash<std::string>{}(str);
std::cout << "hash(" << std::quoted(str) << ") = " << str_hash << std::endl;

str = "Meet the new boss..,";
str_hash = std::hash<std::string>{}(str);
std::cout << "hash(" << std::quoted(str) << ") = " << str_hash << std::endl;

return 0;
}

Windows, VS 2017 的结果:

hash(“Meet the new boss…”) = 5935324269489717502

hash(“Meet the new boss..;”) = 5935347359233909933

hash(“Meet the new boss../“) = 5935325369001345713

hash(“Meet the new boss..,”) = 5935322070466461080

Ubuntu 16.04, g++ 5.4.0 20160609 的结果:

hash(“Meet the new boss…”) = 10656026664466977650

hash(“Meet the new boss..;”) = 12509209616339026574

hash(“Meet the new boss../“) = 6552276210272946664

hash(“Meet the new boss..,”) = 15639609178671340058

还好我们不会在生产环境,使用 Windows 部署 MongoDB……

1
2
3
std::size_t ShardId::Hasher::operator()(const ShardId& shardId) const {
return std::hash<std::string>()(shardId._shardId);
}

详见:https://github.com/mongodb/mongo/blob/master/src/mongo/s/shard_id.cpp

这个 std::hash 在 x86 和 x64 下都不一样,所以,让我们看看 MongoDB 如何解决这个问题:

MongoDB 3.4 no longer supports 32-bit x86 platforms.

好样的!

求模版函数地址

最近用 WTL 写 Ribbon 界面,发现一个坑。

先看 WTL9.1 的代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
static void (CharFormat::*Getk_[])(IPropertyStore*) = 
{
&CharFormat::Getk_Family,
&CharFormat::Getk_FontProperties_Size,
&CharFormat::Getk_MaskEffect<CFM_BOLD, CFE_BOLD, UI_PKEY_FontProperties_Bold>,
&CharFormat::Getk_MaskEffect<CFM_ITALIC, CFE_ITALIC, UI_PKEY_FontProperties_Italic>,
&CharFormat::Getk_MaskEffect<CFM_UNDERLINE, CFE_UNDERLINE, UI_PKEY_FontProperties_Underline>,
&CharFormat::Getk_MaskEffect<CFM_STRIKEOUT, CFE_STRIKEOUT, UI_PKEY_FontProperties_Strikethrough>,
&CharFormat::Getk_VerticalPositioning,
&CharFormat::Getk_Color<CFM_COLOR, UI_PKEY_FontProperties_ForegroundColor>,
&CharFormat::Getk_Color<CFM_BACKCOLOR, UI_PKEY_FontProperties_BackgroundColor>,
&CharFormat::Getk_ColorType<CFM_COLOR, CFE_AUTOCOLOR, UI_SWATCHCOLORTYPE_AUTOMATIC, UI_PKEY_FontProperties_ForegroundColorType>,
&CharFormat::Getk_ColorType<CFM_BACKCOLOR, CFE_AUTOBACKCOLOR, UI_SWATCHCOLORTYPE_NOCOLOR, UI_PKEY_FontProperties_BackgroundColorType>,
};

其中 Getk_MaskEffect 是个模版函数,实现如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
template <DWORD t_dwMask, DWORD t_dwEffects, REFPROPERTYKEY key>
void Getk_MaskEffect(IPropertyStore* pStore)
{
if (SUCCEEDED(pStore->GetValue(key, &propvar)))
{
UIPropertyToUInt32(key, propvar, &uValue);
if ((UI_FONTPROPERTIES)uValue != UI_FONTPROPERTIES_NOTAVAILABLE)
{
dwMask |= t_dwMask;
dwEffects |= ((UI_FONTPROPERTIES) uValue == UI_FONTPROPERTIES_SET) ? t_dwEffects : 0;
}
}
}

然后,在 VS2017 编译失败了……

1>X:\WTL91_5321_Final\Include\atlribbon.h(422): error C2440: ‘initializing’: cannot convert from ‘overloaded-function’ to ‘void (__thiscall WTL::RibbonUI::CharFormat:: )(IPropertyStore )’

1>X:\WTL91_5321_Final\Include\atlribbon.h(422): note: None of the functions with this name in scope match the target type

然后根据错误提示搜到:Cannot take address of template function,https://gcc.gnu.org/bugzilla/show_bug.cgi?id=39018,翻译一下:模版函数的地址转化,分两步走,第一步先转具化,第二步转目标类型,这样可以;直接转过去不可以!

再来看看 WTL10 怎么解决这个问题的!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
static void (CharFormat::*Getk_[])(IPropertyStore*) = 
{
&CharFormat::Getk_Family,
&CharFormat::Getk_FontProperties_Size,
&CharFormat::Getk_MaskEffectBold,
&CharFormat::Getk_MaskEffectItalic,
&CharFormat::Getk_MaskEffectUnderline,
&CharFormat::Getk_MaskEffectStrikeout,
&CharFormat::Getk_VerticalPositioning,
&CharFormat::Getk_Color,
&CharFormat::Getk_ColorBack,
&CharFormat::Getk_ColorType,
&CharFormat::Getk_ColorTypeBack,
};

原来的模版函数,已经替换成普通函数了……

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
void Getk_MaskEffectBold(IPropertyStore* pStore)
{
Getk_MaskEffectAll(pStore, CFM_BOLD, CFE_BOLD, UI_PKEY_FontProperties_Bold);
}

void Getk_MaskEffectItalic(IPropertyStore* pStore)
{
Getk_MaskEffectAll(pStore, CFM_ITALIC, CFE_ITALIC, UI_PKEY_FontProperties_Italic);
}

void Getk_MaskEffectUnderline(IPropertyStore* pStore)
{
Getk_MaskEffectAll(pStore, CFM_UNDERLINE, CFE_UNDERLINE, UI_PKEY_FontProperties_Underline);
}

void Getk_MaskEffectStrikeout(IPropertyStore* pStore)
{
Getk_MaskEffectAll(pStore, CFM_STRIKEOUT, CFE_STRIKEOUT, UI_PKEY_FontProperties_Strikethrough);
}

void Getk_MaskEffectAll(IPropertyStore* pStore, DWORD _dwMask, DWORD _dwEffects, REFPROPERTYKEY key)
{
if (SUCCEEDED(pStore->GetValue(key, &propvar)))
{
UIPropertyToUInt32(key, propvar, &uValue);
if ((UI_FONTPROPERTIES)uValue != UI_FONTPROPERTIES_NOTAVAILABLE)
{
dwMask |= _dwMask;
dwEffects |= ((UI_FONTPROPERTIES)uValue == UI_FONTPROPERTIES_SET) ? _dwEffects : 0;
}
}
}

Mongo Shell 下批量更新集合

需求

延长 mongodb 某集合里的“过期时间”字段。

风险分析

update 一下是很简单,主要怕在 Shell 下操作可能改变数字类型。
先做了实验,发现 3.2 的版本下,并没有这个问题,之前看书,说数字可能被改为双精度,看来是旧版本的不足。

1
2
3
4
db.UMU.find().forEach(function (doc) {
doc.expireDate = NumberLong(doc.updateTime + 180*24*60*60*1000);
db.UMU.save(doc);
})

其中 NumberLong 是必要的,不然更新后,expireDate 的类型并不是和 updateTime 一样的 NumberLong。

云录音

在云游戏和云桌面项目中,总结了几类声音采集技术,把录音做到极致。

从外设录音

最典型的就是麦克风,内置麦克风、外置麦克风,其实还有一种通过 LineIn 插入的其它播放器设备,比如 CD、DVD 等。

采集这种音频的方法可以只用 ffmpeg 搞定:av_find_input_format(“dshow”)…,也可以用 CoreAudio 搞定:

1
2
enumerator->EnumAudioEndpoints(eCapture, DEVICE_STATE_ACTIVE, ...
audio_client->Initialize(AUDCLNT_SHAREMODE_SHARED, AUDCLNT_STREAMFLAGS_EVENTCALLBACK ...

从播放设备回放录音

采集方式是用 CoreAudio:

1
2
enumerator->(eRender, DEVICE_STATE_ACTIVE, ...
audio_client->Initialize(AUDCLNT_SHAREMODE_SHARED, AUDCLNT_STREAMFLAGS_LOOPBACK, ...

这种方式会混音,比如说您开个 foobar 播歌,再开个 QQ 影音看电影,则会录到这两个应用程序的混音,嗯,如果 QQ 再嘀嘀嘀,也是会混进去的……

虚拟声卡采集

有个叫 Virtual Audio Cable 的虚拟声卡,能虚拟多张声卡,并且可以把声音转发到对应的虚拟 LineIn 设备,供应用程序采集。

只录制某个应用程序的音

比前一种更先进一些,多个播放器同时播歌,我们可以只录其中一个。

采集方法是:Hook CoreAudio。

另一个思路是:Hook 到这个应用,给它单独指定一个输出设备,其它应用不能用,否则还是混音了,然后用前面的回放录音技术录制这个独占的输出设备。您可能要说,哪有那么多输出设备?这个问题可以用前面提到的虚拟声卡解决,分分秒虚拟出 64 个是没问题的。而且用 VAC 的好处是,可以在这 64 个对应的 LineIn 通道直接录制,不需要用 CoreAudio,兼容性会更好。

完全免费的 Windows Server 系统,不需要序列号、不需要激活、更不需要破解

2009-04-17 22:06 在百度空间上发表过一次,后来百度空间倒闭了……最近给自己家里搭建家庭文件共享服务器用到,所以在这边再发一次。

2009 年时,由于项目需要,用过 Hyper-V Server 2008。到了 2012-09-25 升级为 Hyper-V Server 2012。这次(2017-03-22)用的是 Hyper-V Server 2016。这么多年一直还是完全免费的。

Hyper-V Server 是基于 Windows Server Server Core x64 的虚拟机服务器系统,要正常提供虚拟机服务, CPU 必须满足三个条件:x64、DEP (Data Execution Prevention)、HV (Hardware Virtualization),但 UMU 不需要它的专业本领——虚拟机服务,所以只需要有 x64 CPU 就可以了。目前只使用他的副业,作为网上邻居(SMB)服务器和静态文件 HTTP Server,就家用而言,绝对够用,前者是系统自带的共享功能,用 net share 命令开启,后者安装 node.js + http-server 模块。

但它不是完整的 Windows Server,比如您想跑 IIS,那就不能使用它了。它最适合的情况是您开发了一些系统服务(NT Service)类的应用,比如游戏服务端、聊天软件服务端,想发布到 Windows Server 上。

Lenovo Y1S 千兆 LAN 改 WAN

需求

Lenovo Y1S 的原 WAN 口是百兆的,连接千兆网络时,WAN 口成为瓶颈,需把两个千兆 LAN 其中一个改为 WAN。

实现

UMU 一开始直接在官方 ROM 上去改 switch,却发现行不通,因为原 WAN 口被废掉后,如果不插着网线,官方 ROM 会很智能地以为路由器是没上网的,子网内终端浏览网页时,会一直被重定向到路由器设置页面。

尝试修改 /etc/config/system 还是没解决,所以……直接刷 OpenWRT 吧……三步走:

  1. 参考《newifi mini 刷 OpenWRT》。

  2. 下载 ROM。开发版地址如下,稳定版请根据当前情况自寻链接。

https://downloads.openwrt.org/snapshots/trunk/ramips/mt7620/openwrt-ramips-mt7620-y1s-squashfs-sysupgrade.bin

  1. 刷完再改 switch,就能搞定。

newifi mini 刷 OpenWRT

newifi mini,即 lenovo Y1,属于不开放 SSH 的类型,越用越不爽,还是刷了吧。

先到 http://www.xcloud.cc/download.shtml 下载“路由助手”,然后到 OpenWRT 官网下载 ROM,推荐用 trunk 上的(目前是 dd),因为 bb 和 cc 都没有集成 kmod-mt76(当然你自己手动安装是可以的,opkg install kmod-mt76),下载链接:http://downloads.openwrt.org/snapshots/trunk/ramips/mt7620/openwrt-ramips-mt7620-y1-squashfs-sysupgrade.bin。目前刷完是 OpenWrt Designated Driver r47548,5G WiFi 没问题。

由于是 trunk 版,luci 可能要自己安装,请参考文章《跟 UMU 一起玩 OpenWRT(入门篇7):安装 LUCI》。

存在几个问题:

  1. 刷完,三个网口顺序和原版是颠倒的。

  2. 5G WiFi 设置参数后似乎没有办法立刻生效,UMU 都是 reboot 一下解决。

跟 UMU 一起玩 OpenWRT(入门篇7):安装 LUCI

UMU 不推荐安装 LUCI,还是多打命令好,可以学习更多东西,而且 LUCI 比较浪费存储空间!

1
2
opkg update
opkg install luci-ssl

推荐使用 SSL 版本,比较安全,但比较大,如果装不下可以试试不带 SSL 的:

1
opkg install luci

您可能不习惯默认的主题(luci-theme-bootstrap),Flash 够大的话,还是装个常用的:

1
opkg install luci-theme-openwrt

开启服务:

1
/etc/init.d/uhttpd start

设置开机自动运行(不推荐):

1
/etc/init.d/uhttpd enable