ZeroNet Blogs

Static ZeroNet blogs mirror

最近本咸找到了Anonymous Hub——本咸遇到的首个以及目前为止本咸见过的唯一一个接受并分发匿名账户消息的ZeroMe Hub.

目前AnonymousHub接受的用户证书提供者有两个:@0ch.anonymous 和 @ifs.anonymous。(没错,这玩意拒绝接受普通的ZeroID用户)

据AnonymousHub的介绍,这个Hub由Mr.Anonymous创建和维护,但他并没有透露他的ZeroID,所以目前只能通过在0me中圈他来进行沟通。

(不过Mr.Anonymous的备注是中英双语书写,所以应该是个国人。)

本咸对这个新奇的玩意很有性趣,所以在此记录一下本咸探索出来的AnonymousHub的特性。

那么,壕戏开场啦!


1、据AnonymousHub页面的介绍,匿名者和普通用户无法直接交流(也就是匿名者在普通用户帖子下的评论(包括赞)无法被包括其它匿名者在内的任何人看到,反之亦然)(本机用户除外,所以有公共账号的公共代理可以一窥在其它帖子下玩耍的本机匿名者)。且本咸鱼通过实验也证实这个特性的确存在。

2、ZeroMe的用户搜索中无法找到匿名者们的信息。

3、即使作为匿名者,你仍然可以在0Hello的信息流中知道你被人回复以及谁提到了你。~~(但是这在默认情况下有不提醒的概率,可能需要额外的手动配置)~~原因刨出!见下

===以上来自2017-8-1===

4、由于0me站点的机制,每个0me站点在同一时刻只能对一个账户的信息流进行推送以及提醒,换言之,如果你想同时收到多个0me号的提醒,你就需要添加多个0me站点并在其中使用不同的账户。←←←2017-8-9

5、普通人关注的匿名者并不会出现在profile的关注列表中。←←←2017-8-9

目前就知道这些,接下来本咸发现了AnonymousHub的其它特性也会记录于此并打上时间戳。

主要收录个人博客,如果是色情站,那个色情站暂时不收录,哈哈哈。欢迎像我一样的个人博客网友互链。

腾讯QQ群里的机器人,说了不正确的话,惨遭调整。默哀一分钟


image alt image alt image alt

包括ZeroNet的配置、管理以及服务器运维等等

​​​​​​​https://www.zerogate.tk/yangleitj.bit/

麻麻再也不会说我整天趴在电脑前了😅


效果图:

Insert

环境:电脑,有个路由器

Windows上:

在ZeroNet的目录下新建一个txt,里面写入ZeroNet.exe --ui_ip 0.0.0.0。保存后将后缀名改为cmd或者bat,双击运行,这时电脑应该会弹出一个浏览器显示ZeroNet的管理页面。

手机和电脑连在同一个路由器上,在手机浏览器的地址栏写入电脑的IP地址以及服务的端口192.168.1.105:43110,就可以在手机上浏览ZeroNet了。

Ubuntu虚拟机上:

若是在Ubuntu虚拟机运行ZeroNet,则虚拟机的网络适配器应该设置为桥接模式,假设Ubuntu的IP地址为192.168.1.109。电脑和手机连接在同一个WiFi上,在ZeroNet的目录下运行python zeronet.py --ui_ip 0.0.0.0,在手机浏览器输入192.168.1.109:43110就可以了~~

🙂

这里还有个进阶教程:搭建Https的ZeroNet节点反向代理

Emoji !!!

- Posted in What I know中文博客 by with comments

😀 😀 😀


想要使用emoji表情,复制粘贴就可以啦~

If you want to use emoji,just copy and paste it !

😀 😃 😄 😁 😆 😅 😂 🤣 ☺️ 😊 😇 🙂 🙃 😉 😌 😍 😘 😗 😙 😚 😋 😜 😝 😛 🤑 🤗 🤓 😎 🤡 🤠 😏 😒 😞 😔 😟 😕 🙁 ☹️ 😣 😖 😫 😩 😤 😠 😡 😶 😐 😑 😯 😦 😧 😮 😲 😵 😳 😱 😨 😰 😢 😥 🤤 😭 😓 😪 😴 🙄 🤔 🤥 😬 🤐 🤢 🤧 😷 🤒 🤕 😈 👿 👹 👺 💩 👻 💀 ☠️ 👽 👾 🤖 🎃 😺 😸 😹 😻 😼 😽 🙀 😿 😾 👐 🙌 👏 🙏 🤝 👍 👎 👊 ✊ 🤛 🤜 🤞 ✌️ 🤘 👌 👈 👉 👆 👇 ☝️ ✋ 🤚 🖐 🖖 👋 🤙 💪 🖕 ✍️ 🤳 💅 🖖 💄 💋 👄 👅 👂 👃 👣 👁 👀 🗣 👤 👥 👶 👦 👧 👨 👩 👱‍♀️ 👱 👴 👵 👲 👳‍♀️ 👳 👮‍♀️ 👮 👷‍♀️ 👷 💂‍♀️ 💂 🕵️‍♀️ 🕵️ 👩‍⚕️ 👨‍⚕️ 👩‍🌾 👨‍🌾 👩‍🍳 👨‍🍳 👩‍🎓 👨‍🎓 👩‍🎤 👨‍🎤 👩‍🏫 👨‍🏫 👩‍🏭 👨‍🏭 👩‍💻 👨‍💻 👩‍💼 👨‍💼 👩‍🔧 👨‍🔧 👩‍🔬 👨‍🔬 👩‍🎨 👨‍🎨 👩‍🚒 👨‍🚒 👩‍✈️ 👨‍✈️ 👩‍🚀 👨‍🚀 👩‍⚖️ 👨‍⚖️ 🤶 🎅 👸 🤴 👰 🤵 👼 🤰 🙇‍♀️ 🙇 💁 💁‍♂️ 🙅 🙅‍♂️ 🙆 🙆‍♂️ 🙋 🙋‍♂️ 🤦‍♀️ 🤦‍♂️ 🤷‍♀️ 🤷‍♂️ 🙎 🙎‍♂️ 🙍 🙍‍♂️ 💇 💇‍♂️ 💆 💆‍♂️ 🕴 💃 🕺 👯 👯‍♂️ 🚶‍♀️ 🚶 🏃‍♀️ 🏃 👫 👭 👬 💑 👩‍❤️‍👩 👨‍❤️‍👨 💏 👩‍❤️‍💋‍👩 👨‍❤️‍💋‍👨 👪 👨‍👩‍👧 👨‍👩‍👧‍👦 👨‍👩‍👦‍👦 👨‍👩‍👧‍👧 👩‍👩‍👦 👩‍👩‍👧 👩‍👩‍👧‍👦 👩‍👩‍👦‍👦 👩‍👩‍👧‍👧 👨‍👨‍👦 👨‍👨‍👧 👨‍👨‍👧‍👦 👨‍👨‍👦‍👦 👨‍👨‍👧‍👧 👩‍👦 👩‍👧 👩‍👧‍👦 👩‍👦‍👦 👩‍👧‍👧 👨‍👦 👨‍👧 👨‍👧‍👦 👨‍👦‍👦 👨‍👧‍👧 👚 👕 👖 👔 👗 👙 👘 👠 👡 👢 👞 👟 👒 🎩 🎓 👑 ⛑ 🎒 👝 👛 👜 💼 👓 🕶 🌂 ☂️

👐🏻 🙌🏻 👏🏻 🙏🏻 👍🏻 👎🏻 👊🏻 ✊🏻 🤛🏻 🤜🏻 🤞🏻 ✌🏻 🤘🏻 👌🏻 👈🏻 👉🏻 👆🏻 👇🏻 ☝🏻 ✋🏻 🤚🏻 🖐🏻 🖖🏻 👋🏻 🤙🏻 💪🏻 🖕🏻 ✍🏻 🤳🏻 💅🏻 👂🏻 👃🏻 👶🏻 👦🏻 👧🏻 👨🏻 👩🏻 👱🏻‍♀️ 👱🏻 👴🏻 👵🏻 👲🏻 👳🏻‍♀️ 👳🏻 👮🏻‍♀️ 👮🏻 👷🏻‍♀️ 👷🏻 💂🏻‍♀️ 💂🏻 🕵🏻‍♀️ 🕵🏻 👩🏻‍⚕️ 👨🏻‍⚕️ 👩🏻‍🌾 👨🏻‍🌾 👩🏻‍🍳 👨🏻‍🍳 👩🏻‍🎓 👨🏻‍🎓 👩🏻‍🎤 👨🏻‍🎤 👩🏻‍🏫 👨🏻‍🏫 👩🏻‍🏭 👨🏻‍🏭 👩🏻‍💻 👨🏻‍💻 👩🏻‍💼 👨🏻‍💼 👩🏻‍🔧 👨🏻‍🔧 👩🏻‍🔬 👨🏻‍🔬 👩🏻‍🎨 👨🏻‍🎨 👩🏻‍🚒 👨🏻‍🚒 👩🏻‍✈️ 👨🏻‍✈️ 👩🏻‍🚀 👨🏻‍🚀 👩🏻‍⚖️ 👨🏻‍⚖️ 🤶🏻 🎅🏻 👸🏻 🤴🏻 👰🏻 🤵🏻 👼🏻 🤰🏻 🙇🏻‍♀️ 🙇🏻 💁🏻 💁🏻‍♂️ 🙅🏻 🙅🏻‍♂️ 🙆🏻 🙆🏻‍♂️ 🙋🏻 🙋🏻‍♂️ 🤦🏻‍♀️ 🤦🏻‍♂️ 🤷🏻‍♀️ 🤷🏻‍♂️ 🙎🏻 🙎🏻‍♂️ 🙍🏻 🙍🏻‍♂️ 💇🏻 💇🏻‍♂️ 💆🏻 💆🏻‍♂️ 🕴🏻 💃🏻 🕺🏻 🚶🏻‍♀️ 🚶🏻 🏃🏻‍♀️ 🏃🏻 🏋🏻‍♀️ 🏋🏻 🤸🏻‍♀️ 🤸🏻‍♂️ ⛹🏻‍♀️ ⛹🏻 🤾🏻‍♀️ 🤾🏻‍♂️ 🏌🏻‍♀️ 🏌🏻 🏄🏻‍♀️ 🏄🏻 🏊🏻‍♀️ 🏊🏻 🤽🏻‍♀️ 🤽🏻‍♂️ 🚣🏻‍♀️ 🚣🏻 🏇🏻 🚴🏻‍♀️ 🚴🏻 🚵🏻‍♀️ 🚵🏻 🤹🏻‍♀️ 🤹🏻‍♂️ 🛀🏻

🐶 🐱 🐭 🐹 🐰 🦊 🐻 🐼 🐨 🐯 🦁 🐮 🐷 🐽 🐸 🐵 🙊 🙉 🙊 🐒 🐔 🐧 🐦 🐤 🐣 🐥 🦆 🦅 🦉 🦇 🐺 🐗 🐴 🦄 🐝 🐛 🦋 🐌 🐚 🐞 🐜 🕷 🕸 🐢 🐍 🦎 🦂 🦀 🦑 🐙 🦐 🐠 🐟 🐡 🐬 🦈 🐳 🐋 🐊 🐆 🐅 🐃 🐂 🐄 🦌 🐪 🐫 🐘 🦏 🦍 🐎 🐖 🐐 🐏 🐑 🐕 🐩 🐈 🐓 🦃 🕊 🐇 🐁 🐀 🐿 🐾 🐉 🐲 🌵 🎄 🌲 🌳 🌴 🌱 🌿 ☘️ 🍀 🎍 🎋 🍃 🍂 🍁 🍄 🌾 💐 🌷 🌹 🥀 🌻 🌼 🌸 🌺 🌎 🌍 🌏 🌕 🌖 🌗 🌘 🌑 🌒 🌓 🌔 🌚 🌝 🌞 🌛 🌜 🌙 💫 ⭐️ 🌟 ✨ ⚡️ 🔥 💥 ☄️ ☀️ 🌤 ⛅️ 🌥 🌦 🌈 ☁️ 🌧 ⛈ 🌩 🌨 ☃️ ⛄️ ❄️ 🌬 💨 🌪 🌫 🌊 💧 💦 ☔️

🍏 🍎 🍐 🍊 🍋 🍌 🍉 🍇 🍓 🍈 🍒 🍑 🍍 🥝 🥑 🍅 🍆 🥒 🥕 🌽 🌶 🥔 🍠 🌰 🥜 🍯 🥐 🍞 🥖 🧀 🥚 🍳 🥓 🥞 🍤 🍗 🍖 🍕 🌭 🍔 🍟 🥙 🌮 🌯 🥗 🥘 🍝 🍜 🍲 🍥 🍣 🍱 🍛 🍚 🍙 🍘 🍢 🍡 🍧 🍨 🍦 🍰 🎂 🍮 🍭 🍬 🍫 🍿 🍩 🍪 🥛 🍼 ☕️ 🍵 🍶 🍺 🍻 🥂 🍷 🥃 🍸 🍹 🍾 🥄 🍴 🍽

⚽️ 🏀 🏈 ⚾️ 🎾 🏐 🏉 🎱 🏓 🏸 🥅 🏒 🏑 🏏 ⛳️ 🏹 🎣 🥊 🥋 ⛸ 🎿 ⛷ 🏂 🏋️‍♀️ 🏋️ 🤺 🤼‍♀️ 🤼‍♂️ 🤸‍♀️ 🤸‍♂️ ⛹️‍♀️ ⛹️ 🤾‍♀️ 🤾‍♂️ 🏌️‍♀️ 🏌️ 🏄‍♀️ 🏄 🏊‍♀️ 🏊 🤽‍♀️ 🤽‍♂️ 🚣‍♀️ 🚣 🏇 🚴‍♀️ 🚴 🚵‍♀️ 🚵 🎽 🏅 🎖 🥇 🥈 🥉 🏆 🏵 🎗 🎫 🎟 🎪 🤹‍♀️ 🤹‍♂️ 🎭 🎨 🎬 🎤 🎧 🎼 🎹 🥁 🎷 🎺 🎸 🎻 🎲 🎯 🎳 🎮 🎰

🚗 🚕 🚙 🚌 🚎 🏎 🚓 🚑 🚒 🚐 🚚 🚛 🚜 🛴 🚲 🛵 🏍 🚨 🚔 🚍 🚘 🚖 🚡 🚠 🚟 🚃 🚋 🚞 🚝 🚄 🚅 🚈 🚂 🚆 🚇 🚊 🚉 🚁 🛩 ✈️ 🛫 🛬 🚀 🛰 💺 🛶 ⛵️ 🛥 🚤 🛳 ⛴ 🚢 ⚓️ 🚧 ⛽️ 🚏 🚦 🚥 🗺 🗿 🗽 ⛲️ 🗼 🏰 🏯 🏟 🎡 🎢 🎠 ⛱ 🏖 🏝 ⛰ 🏔 🗻 🌋 🏜 🏕 ⛺️ 🛤 🛣 🏗 🏭 🏠 🏡 🏘 🏚 🏢 🏬 🏣 🏤 🏥 🏦 🏨 🏪 🏫 🏩 💒 🏛 ⛪️ 🕌 🕍 🕋 ⛩ 🗾 🎑 🏞 🌅 🌄 🌠 🎇 🎆 🌇 🌆 🏙 🌃 🌌 🌉 🌁

⌚️ 📱 📲 💻 ⌨️ 🖥 🖨 🖱 🖲 🕹 🗜 💽 💾 💿 📀 📼 📷 📸 📹 🎥 📽 🎞 📞 ☎️ 📟 📠 📺 📻 🎙 🎚 🎛 ⏱ ⏲ ⏰ 🕰 ⌛️ ⏳ 📡 🔋 🔌 💡 🔦 🕯 🗑 🛢 💸 💵 💴 💶 💷 💰 💳 💎 ⚖️ 🔧 🔨 ⚒ 🛠 ⛏ 🔩 ⚙️ ⛓ 🔫 💣 🔪 🗡 ⚔️ 🛡 🚬 ⚰️ ⚱️ 🏺 🔮 📿 💈 ⚗️ 🔭 🔬 🕳 💊 💉 🌡 🚽 🚰 🚿 🛁 🛀 🛎 🔑 🗝 🚪 🛋 🛏 🛌 🖼 🛍 🛒 🎁 🎈 🎏 🎀 🎊 🎉 🎎 🏮 🎐 ✉️ 📩 📨 📧 💌 📥 📤 📦 🏷 📪 📫 📬 📭 📮 📯 📜 📃 📄 📑 📊 📈 📉 🗒 🗓 📆 📅 📇 🗃 🗳 🗄 📋 📁 📂 🗂 🗞 📰 📓 📔 📒 📕 📗 📘 📙 📚 📖 🔖 🔗 📎 🖇 📐 📏 📌 📍 📌 🎌 🏳️ 🏴 🏁 🏳️‍🌈 ✂️ 🖊 🖋 ✒️ 🖌 🖍 📝 ✏️ 🔍 🔎 🔏 🔐 🔒 🔓

❤️ 💛 💚 💙 💜 🖤 💔 ❣️ 💕 💞 💓 💗 💖 💘 💝 💟 ☮️ ✝️ ☪️ 🕉 ☸️ ✡️ 🔯 🕎 ☯️ ☦️ 🛐 ⛎ ♈️ ♉️ ♊️ ♋️ ♌️ ♍️ ♎️ ♏️ ♐️ ♑️ ♒️ ♓️ 🆔 ⚛️ 🉑 ☢️ ☣️ 📴 📳 🈶 🈚️ 🈸 🈺 🈷️ ✴️ 🆚 💮 🉐 ㊙️ ㊗️ 🈴 🈵 🈹 🈲 🅰️ 🅱️ 🆎 🆑 🅾️ 🆘 ❌ ⭕️ 🛑 ⛔️ 📛 🚫 💯 💢 ♨️ 🚷 🚯 🚳 🚱 🔞 📵 🚭 ❗️ ❕ ❓ ❔ ‼️ ⁉️ 🔅 🔆 〽️ ⚠️ 🚸 🔱 ⚜️ 🔰 ♻️ ✅ 🈯️ 💹 ❇️ ✳️ ❎ 🌐 💠 Ⓜ️ 🌀 💤 🏧 🚾 ♿️ 🅿️ 🈳 🈂️ 🛂 🛃 🛄 🛅 🚹 🚺 🚼 🚻 🚮 🎦 📶 🈁 🔣 ℹ️ 🔤 🔡 🔠 🆖 🆗 🆙 🆒 🆕 🆓 0️⃣ 1️⃣ 2️⃣ 3️⃣ 4️⃣ 5️⃣ 6️⃣ 7️⃣ 8️⃣ 9️⃣ 🔟 🔢 #️⃣ *️⃣ ▶️ ⏸ ⏯ ⏹ ⏺ ⏭ ⏮ ⏩ ⏪ ⏫ ⏬ ◀️ 🔼 🔽 ➡️ ⬅️ ⬆️ ⬇️ ↗️ ↘️ ↙️ ↖️ ↕️ ↔️ ↪️ ↩️ ⤴️ ⤵️ 🔀 🔁 🔂 🔄 🔃 🎵 🎶 ➕ ➖ ➗ ✖️ 💲 💱 ™️ ©️ ®️ 〰️ ➰ ➿ 🔚 🔙 🔛 🔝 ✔️ ☑️ 🔘 ⚪️ ⚫️ 🔴 🔵 🔺 🔻 🔸 🔹 🔶 🔷 🔳 🔲 ▪️ ▫️ ◾️ ◽️ ◼️ ◻️ ⬛️ ⬜️ 🔈 🔇 🔉 🔊 🔔 🔕 📣 📢 👁‍🗨 💬 💭 🗯 ♠️ ♣️ ♥️ ♦️ 🃏 🎴 🀄️ 🕐 🕑 🕒 🕓 🕔 🕕 🕖 🕗 🕘 🕙 🕚 🕛 🕜 🕝 🕞 🕟 🕠 🕡 🕢 🕣 🕤 🕥 🕦 🕧

🏳️ 🏴 🏁 🚩 🏳️‍🌈 🇦🇫 🇦🇽 🇦🇱 🇩🇿 🇦🇸 🇦🇩 🇦🇴 🇦🇮 🇦🇶 🇦🇬 🇦🇷 🇦🇲 🇦🇼 🇦🇺 🇦🇹 🇦🇿 🇧🇸 🇧🇭 🇧🇩 🇧🇧 🇧🇾 🇧🇪 🇧🇿 🇧🇯 🇧🇲 🇧🇹 🇧🇴 🇧🇦 🇧🇼 🇧🇷 🇮🇴 🇻🇬 🇧🇳 🇧🇬 🇧🇫 🇧🇮 🇰🇭 🇨🇲 🇨🇦 🇮🇨 🇨🇻 🇧🇶 🇰🇾 🇨🇫 🇹🇩 🇨🇱 🇨🇳 🇨🇽 🇨🇨 🇨🇴 🇰🇲 🇨🇬 🇨🇩 🇨🇰 🇨🇷 🇨🇮 🇭🇷 🇨🇺 🇨🇼 🇨🇾 🇨🇿 🇩🇰 🇩🇯 🇩🇲 🇩🇴 🇪🇨 🇪🇬 🇸🇻 🇬🇶 🇪🇷 🇪🇪 🇪🇹 🇪🇺 🇫🇰 🇫🇴 🇫🇯 🇫🇮 🇫🇷 🇬🇫 🇵🇫 🇹🇫 🇬🇦 🇬🇲 🇬🇪 🇩🇪 🇬🇭 🇬🇮 🇬🇷 🇬🇱 🇬🇩 🇬🇵 🇬🇺 🇬🇹 🇬🇬 🇬🇳 🇬🇼 🇬🇾 🇭🇹 🇭🇳 🇭🇰 🇭🇺 🇮🇸 🇮🇳 🇮🇩 🇮🇷 🇮🇶 🇮🇪 🇮🇲 🇮🇱 🇮🇹 🇯🇲 🇯🇵 🎌 🇯🇪 🇯🇴 🇰🇿 🇰🇪 🇰🇮 🇽🇰 🇰🇼 🇰🇬 🇱🇦 🇱🇻 🇱🇧 🇱🇸 🇱🇷 🇱🇾 🇱🇮 🇱🇹 🇱🇺 🇲🇴 🇲🇰 🇲🇬 🇲🇼 🇲🇾 🇲🇻 🇲🇱 🇲🇹 🇲🇭 🇲🇶 🇲🇷 🇲🇺 🇾🇹 🇲🇽 🇫🇲 🇲🇩 🇲🇨 🇲🇳 🇲🇪 🇲🇸 🇲🇦 🇲🇿 🇲🇲 🇳🇦 🇳🇷 🇳🇵 🇳🇱 🇳🇨 🇳🇿 🇳🇮 🇳🇪 🇳🇬 🇳🇺 🇳🇫 🇰🇵 🇲🇵 🇳🇴 🇴🇲 🇵🇰 🇵🇼 🇵🇸 🇵🇦 🇵🇬 🇵🇾 🇵🇪 🇵🇭 🇵🇳 🇵🇱 🇵🇹 🇵🇷 🇶🇦 🇷🇪 🇷🇴 🇷🇺 🇷🇼 🇼🇸 🇸🇲 🇸🇦 🇸🇳 🇷🇸 🇸🇨 🇸🇱 🇸🇬 🇸🇽 🇸🇰 🇸🇮 🇬🇸 🇸🇧 🇸🇴 🇿🇦 🇰🇷 🇸🇸 🇪🇸 🇱🇰 🇧🇱 🇸🇭 🇰🇳 🇱🇨 🇵🇲 🇻🇨 🇸🇩 🇸🇷 🇸🇿 🇸🇪 🇨🇭 🇸🇾 🇹🇼 🇹🇯 🇹🇿 🇹🇭 🇹🇱 🇹🇬 🇹🇰 🇹🇴 🇹🇹 🇹🇳 🇹🇷 🇹🇲 🇹🇨 🇹🇻 🇻🇮 🇺🇬 🇺🇦 🇦🇪 🇬🇧 🇺🇸 🇺🇾 🇺🇿 🇻🇺 🇻🇦 🇻🇪 🇻🇳 🇼🇫 🇪🇭 🇾🇪 🇿🇲 🇿🇼

😃💁 People🐻🌻 Animals🍔🍹 Food🎷⚽️ Activities🚘🌇 Travel💡🎉 Objects💖🔣 Symbols🎌🏳️‍🌈 Flags

🤣 🤠 🤡 🤥 🤤 🤢 🤧 🤴 🤶 🤵 🤷 🤦 🤰 🕺 🤳 🤞 🤙 🤛 🤜 🤚 🤝 🖤 🦍 🦊 🦌 🦏 🦇 🦅 🦆 🦉 🦎 🦈 🦐 🦑 🦋 🥀 🥝 🥑 🥔 🥕 🥒 🥜 🥐 🥖 🥞 🥓 🥙 🥚 🥘 🥗 🥛 🥂 🥃 🥄 🛑 🛴 🛵 🛶 🥇 🥈 🥉 🥊 🥋 🤸 🤼 🤽 🤾 🤺 🥅 🤹 🥁 🛒

☺️ ☹ ☝️ ✌️ ✍️ ❤️ ❣️ ☠ ♨️ ✈️ ⌛ ⌚ ♈ ♉ ♊ ♋ ♌ ♍ ♎ ♏ ♐ ♑ ♒ ♓ ☀️ ☁️ ☂️ ❄️ ⛄️ ☄ ♠️ ♥️ ♦️ ♣️ ▶️ ◀️ ☎️ ⌨ ✉️ ✏️ ✒️ ✂️ ↗️ ➡️ ↘️ ↙️ ↖️ ↕️ ↔️ ↩️ ↪️ ✡️ ☸ ☯️ ✝️ ☦ ☪ ☮ ☢ ☣ ☑️ ✔️ ✖️ ✳️ ✴️ ❇️ ‼️ ©️ ®️ ™️ Ⓜ️ ▪️ ▫️ #⃣️ *️⃣ 0⃣️ 1⃣️ 2⃣️ 3⃣️ 4⃣️ 5⃣️ 6⃣️ 7⃣️ 8⃣️ 9⃣️ ⁉️ ℹ️ ⤴️ ⤵️ ♻️ ◻️ ◼️ ◽ ◾ ☕ ⚠️ ☔ ⏏ ⬆️ ⬇️ ⬅️ ⚡ ☘ ⚓ ♿ ⚒ ⚙ ⚗ ⚖ ⚔ ⚰ ⚱ ⚜ ⚛ ⚪ ⚫ 🀄 ⭐ ⬛ ⬜ ⛑ ⛰ ⛪ ⛲ ⛺ ⛽ ⛵ ⛴ ⛔ ⛅ ⛈ ⛱ ⛄ ⚽ ⚾️ ⛳ ⛸ ⛷ ⛹ ⛏ ⛓ ⛩ ⭕ ❗ 🅿️ ❦ ♕ ♛ ♔ ♖ ♜ ☾ → ⇒ ⟹ ⇨ ⇰ ➩ ➪ ➫ ➬ ➭ ➮ ➯ ➲ ➳ ➵ ➸ ➻ ➺ ➼ ➽ ☜ ☟ ➹ ➷ ↶ ↷ ✆ ⌘ ⎋ ⏎ ⏏ ⎈ ⎌ ⍟ ❥ ツ ღ ☻

来源:http://getemoji.com/

这里还有更详细的Emoji表情 http://www.unicode.org/emoji/charts-5.0/full-emoji-list.html

想要为ZeroNet做贡献,手里又有Linux的VPS,那这篇文章应该比较合适了。


首先,根据GitHub的提示,下载并且安装ZeroNet

我的服务器是基于Debian的Ubuntu,所以:

sudo apt-get update

sudo apt-get install msgpack-python python-gevent

wget <https://github.com/HelloZeroNet/ZeroNet/archive/master.tar.gz>

tar xvpfz master.tar.gz

cd ZeroNet-master

其他发行版参考github的说明 https://github.com/HelloZeroNet/ZeroNet

进入ZeroNet-master目录后,可以发现目录下有这些东西

CHANGELOG.md        Dockerfile  plugins/         requirements.txt  tools/       zeronet.conf
CHANGELOG-zh-cn.md  LICENSE     README.md        src/              update.py    zeronet.py*
data/               log/        README-zh-cn.md  start.py          Vagrantfile

为了便于描述,假设服务器地址是11.22.33.44,电脑地址为55.66.77.88

执行python zeronet.py则程序会监听127.0.0.1:43110,这个地址只能被本地访问,在电脑的浏览器中输入“http://11.22.33.44:43110”是不能访问到Zeronet的管理界面的。

要想实现在自己的电脑上通过网页控制服务器上运行的Zeronet,应该python zeronet --ui_ip 11.22.33.44,这样,所有联网的设备都可以访问到服务器上运行的Zeronet网页了。

但是,这样便有了安全问题,别人通过你的服务器浏览Zeronet,会导致服务器缓存你不想缓存的内容,而且别人也可以修改、删除你的东西。所以就有了访问控制。

希望只允许55.66.77.88通过58774端口访问服务器上的Zeronet网页,应该这样python zeronet.py --ui_ip 11.22.33.44 --ui_port 58774 --ui_host 55.66.77.88

这样一来,可以访问Zeronet网页的人就减少到1/2^32了。

但这样貌似也没有实现100%的访问控制,毕竟还有很多人和你有着相同的公网IP,我尝试过python zeronet.py,然后用ssh -NL 将端口从服务器转发至本地,但是失败了,显示403,不知为何。所以妥协的方案是在管理完界面后迅速关闭zeronet,以只能本地访问web界面的模式运行。

刚接触Zeronet,发现很有趣,想深入学习,但是发现网上找到的资料很少,于是开始啃官方的help手册。以下参数来自Linux版本的zeronet

python zeronet.py --help


-h, --help show this help message and exit _#显示这个帮助并退出_

--verbose More detailed logging (default: False) #冗余模式,会显示很多详细的日志,默认关闭

--debug Debug mode (default: False) #调试模式,默认关闭

--silent Disable logging to terminal output (default: False) #安静模式,不在终端显示日志,默认关闭

--debug_socket Debug socket connections (default: False) #调试套接字连接,默认关闭

--debug_gevent Debug gevent functions (default: False) #调试gevent函数,默认关闭

--batch Batch mode (No interactive input for commands) (default: False) #批处理模式,该模式下没有命令行交互,默认关闭

--config_file path Path of config file (default: zeronet.conf) #指定配置文件,默认配置文件为zeronet.conf

--data_dir path Path of data directory (default: data) #指定存放数据的目录,默认把数据存储在data目录下(“目录”与“文件夹”同义)

--log_dir path Path of logging directory (default: log) #指定存放日志的文件夹,默认为log目录

--language language Web interface language (default: en) #设置网页语言,默认为英语。这里未指定的话也可以在网页中设置

--ui_ip ip Web interface bind address (default: 127.0.0.1)

#指定网页绑定的ip,默认为127.0.0.1,即浏览器上 “127.0.0.1:43110/”的由来。在默认情况下,这个网页只能被本地访问,如果希望局域网内其他的电脑也能访问,则应该把127.0.0.1改为此电脑在局域网中的IP,如192.168.1.102。
#笔者尝试过将127.0.0.1的43110端口用netcat和ssh –D转发至其他端口以达到其他电脑可以访问的目的,并结合iptables实现访问控制,失败,可能是方法不对。对于有公网IP的服务器,假设IP为11.22.33.45,运行‘python zeronet.py --ui_ip 11.22.33.45’之后,所有联网的电脑都可以通过11.22.33.45:43110访问你的zeronet网页。

--ui_port port Web interface bind port (default: 43110) #指定网页的端口,默认是43110 ,对于一般用户不需要修改。服务器管理员则应当修改,这个端口就相当于Linux的22端口,Windows的3389端口。

--ui_restrict [ip [ip ...]] Restrict web access (default: False) #禁止某些IP访问网页,默认关闭

--ui_host [host [host ...]] Allow access using this hosts (default: None) #只允许某些IP访问网页,默认无。

--open_browser [browser_name] Open homepage in web browser automatically (default: None) #指定自动打开的浏览器,默认无

--homepage address Web interface Homepage (default: 1HeLLo4uzjaLetFx6NH3PMwFP3qbRbTf3D) #指定网页主页,默认为1HeLLo4uzjaLetFx6NH3PMwFP3qbRbTf3D

--updatesite address Source code update site (default: 1UPDatEDxnvHDo7TXvq6AEBARfNkyfxsp) #指定源码更新的站点,默认为1UPDatEDxnvHDo7TXvq6AEBARfNkyfxsp

--size_limit limit Default site size limit in MB (default: 10) #指定每个网站占用磁盘空间的上限,默认为10MB。超过此大小的网站不会全部加载,并会在浏览时发出扩容的请求

--file_size_limit limit Maximum per file size limit in MB (default: 10) #指定每个文件占用磁盘空间的上限,默认10MB。超过此大小的文件不会被下载

--connected_limit connected_limit Max connected peer per site (default: 8) #限制节点的数量,默认每个网站最多连接8个节点

--workers workers Download workers per site (default: 5) #限制下载线程,默认每个网站以5线程下载

--fileserver_ip ip FileServer bind address (default: *) #指定文件服务器地址,默认所有,具体用途不明。

--fileserver_port port FileServer bind port (default: 15441) #指定文件服务绑定端口,默认从15441端口分享数据给别人。如果15441端口被封,就修改这个值为其他端口。

--ip_local [ip [ip ...]] My local ips (default: ['127.0.0.1']) #指定本地IP,默认127.0.0.1,具体用途不知

--disable_udp Disable UDP connections (default: False) #关闭UDP连接,默认不关闭

--proxy ip:port Socks proxy address (default: None) #使用socks代理,默认无

--bind ip Bind outgoing sockets to this address (default: None) #将输出的套接字绑定到某个IP,默认无,具体用途不知

--ip_external ip Set reported external ip (tested on start if None) (default: None) #设置外部IP,如果没有的话启动时会自动探测,默认无

--trackers [protocol://address [protocol://address ...]] Bootstraping torrent trackers (default: ['zero://boot3rdez4rzn36x.onion:15441', 'zero://boot.z eronet.io#f36ca555bee6ba216b14d10f38c16f7769ff064e 0e37d887603548cc2e64191d:15441', 'udp://tracker.coppersurfer.tk:6969', 'udp://tracker .leechers-paradise.org:6969', 'udp://9.rarbg.com:2710', 'http://tracker.opentrackr.org:1337/announce', 'http://explodie.org:6969/announce', 'http://tracker1.wasabii.com.tw:6969/announce']) #设置tracker,默认tracker如上所示。当这些tracker失效或者被屏蔽时,可以指定别的tracker以绕过封锁。

--trackers_file path Load torrent trackers dynamically from a file (default: False) #从文件中动态加载torrent trackers,默认无,具体用途不知

--use_openssl {True,False} Use OpenSSL liblary for speedup (default: True) #使用OpenSSL加速,默认开启加速

--disable_db Disable database updating (default: False) #关闭数据库更新,默认不关闭

--disable_encryption Disable connection encryption (default: False) #连接时不加密,默认加密

--disable_sslcompression {True,False} Disable SSL compression to save memory (default: True) #关闭SSL压缩以节省内存,默认关闭

--keep_ssl_cert Disable new SSL cert generation on startup (default: False) #启动时使用之前的SSL证书,默认不使用之前的SSL证书

--max_files_opened limit Change maximum opened files allowed by OS to this value on startup (default: 2048) #设置启动时打开文件的最大数量,默认是2048个

--stack_size thread_stack_size Change thread stack size (default: None) #改变堆栈大小,默认不改变

--use_tempfiles {True,False} Use temporary files when downloading (experimental) (default: False) #使用下载时的缓存文件,默认不使用,此功能尚在实验阶段

--stream_downloads {True,False} Stream download directly to files (experimental) (default: False) #流式下载/边下边播,默认不开启,此功能尚在实验阶段

--msgpack_purepython {True,False} Use less memory, but a bit more CPU power (default: True) #使用更少的内存,但CPU会占用得高一点。默认开启

--fix_float_decimals {True,False} Fix content.json modification date float precision on verification (default: False) #修复在验证content.json时的准确度,默认不修复

--db_mode {speed,security} #选择数据库模式,更快或者更安全

--coffeescript_compiler executable_path Coffeescript compiler for developing (default: None) #开发用的脚本编译路径

--tor {disable,enable,always} enable: Use only for Tor peers, always: Use Tor for every connection (default: enable) #Tor相关,我不知道enable和always下的区别

--tor_controller ip:port Tor controller address (default: 127.0.0.1:9051) #tor控制器的地址,默认是127.0.0.1:9051

--tor_proxy ip:port Tor proxy address (default: 127.0.0.1:9050) #tor代理的地址,默认是127.0.0.1:9050

--tor_password password Tor controller password (default: None) #tor控制器的密码,默认无密码

--tor_hs_limit limit Maximum number of hidden services (default: 10) #设置tor跳板数最大值,默认10

--version show program's version number and exit #显示版本并退出

--end Stop multi value argument parsing (default: False) #参数结束标志

在编写动态站点时,经常需要收集分散在各个 JSON 文件中的数据。为了方便应用,ZeroNet 提供了由描述文件 dbschema.json 控制的数据库机制,但在文档方面,则只有一份代码样例。尽管代码样例经过注释,但许多细节仍未说明,需要阅读源码。现在我把发现写在这里,这样你就不需要费事啦(≧∇≦)b

这篇文章是关于 ZeroNet 0.5.7 master 分支 rev2169 的分析,可能不适用于其他版本。


version 的具体作用

对于 version 这个键的值,文档是这样记述的:

  • 1 = json 表含有同时包含目录名和文件名的 path
  • 2 = json 表含有包含目录名的 directory 列和包含文件名的 file_name
  • 3 = 与 2 相同,但同时含有 site 列(用于 Merger Site)

……是不是不太明白?嗯。我也不太明白。现在看一看对应的实现:

# src/Db/Db.py#L180
# Check json table
if self.schema["version"] == 1:
    changed = cur.needTable("json", [
        ["json_id", "INTEGER PRIMARY KEY AUTOINCREMENT"],
        ["path", "VARCHAR(255)"]
    ], [
        "CREATE UNIQUE INDEX path ON json(path)"
    ], version=self.schema["version"])
elif self.schema["version"] == 2:
    changed = cur.needTable("json", [
        ["json_id", "INTEGER PRIMARY KEY AUTOINCREMENT"],
        ["directory", "VARCHAR(255)"],
        ["file_name", "VARCHAR(255)"]
    ], [
        "CREATE UNIQUE INDEX path ON json(directory, file_name)"
    ], version=self.schema["version"])
elif self.schema["version"] == 3:
    changed = cur.needTable("json", [
        ["json_id", "INTEGER PRIMARY KEY AUTOINCREMENT"],
        ["site", "VARCHAR(255)"],
        ["directory", "VARCHAR(255)"],
        ["file_name", "VARCHAR(255)"]
    ], [
        "CREATE UNIQUE INDEX path ON json(directory, site, file_name)"
    ], version=self.schema["version"])
if changed:
    changed_tables.append("json")

# Check schema tables
for table_name, table_settings in self.schema["tables"].items():
    changed = cur.needTable(
        table_name, table_settings["cols"],
        table_settings["indexes"], version=table_settings["schema_changed"]
    )
    if changed:
        changed_tables.append(table_name)
# src/Db/DbCursor.py#L118
# Get or create a row for json file
# Return: The database row
def getJsonRow(self, file_path):
    directory, file_name = re.match("^(.*?)/*([^/]*)$", file_path).groups()
    if self.db.schema["version"] == 1:
        # One path field
        res = self.execute("SELECT * FROM json WHERE ? LIMIT 1", {"path": file_path})
        row = res.fetchone()
        if not row:  # No row yet, create it
            self.execute("INSERT INTO json ?", {"path": file_path})
            res = self.execute("SELECT * FROM json WHERE ? LIMIT 1", {"path": file_path})
            row = res.fetchone()
    elif self.db.schema["version"] == 2:
        # Separate directory, file_name (easier join)
        res = self.execute("SELECT * FROM json WHERE ? LIMIT 1", {"directory": directory, "file_name": file_name})
        row = res.fetchone()
        if not row:  # No row yet, create it
            self.execute("INSERT INTO json ?", {"directory": directory, "file_name": file_name})
            res = self.execute("SELECT * FROM json WHERE ? LIMIT 1", {"directory": directory, "file_name": file_name})
            row = res.fetchone()
    elif self.db.schema["version"] == 3:
        # Separate site, directory, file_name (for merger sites)
        site_address, directory = re.match("^([^/]*)/(.*)$", directory).groups()
        res = self.execute("SELECT * FROM json WHERE ? LIMIT 1", {"site": site_address, "directory": directory, "file_name": file_name})
        row = res.fetchone()
        if not row:  # No row yet, create it
            self.execute("INSERT INTO json ?", {"site": site_address, "directory": directory, "file_name": file_name})
            res = self.execute("SELECT * FROM json WHERE ? LIMIT 1", {"site": site_address, "directory": directory, "file_name": file_name})
            row = res.fetchone()
    else:
        raise Exception("Dbschema version %s not supported" % self.db.schema.get("version"))
    return row

可以看到,version 的值看上去有两项作用:其一是设置 json 表的结构和索引,其二是决定插入 json 行时的行为:

  • path - 仅在 version 为 1 时设置,包含完整的路径。
  • directory - 在 version 为 2 或 3 时设置,包含文件所在文件夹的路径。不包含最后的分隔符。
  • file_name - 在 version 为 2 或 3 时设置,包含文件名。
  • site - 仅在 version 为 3 时设置,包含文件所在站点的公钥地址。

然而进一步观察,就可以发现 json 表的创建和紧接着的其他数据表的创建均调用了同一个方法 needTable。那么,这个方法又是如何实现的呢?

# src/Db/DbCursor.py#L101
# Create table if not exist
# Return: True if updated
def needTable(self, table, cols, indexes=None, version=1):
    current_version = self.db.getTableVersion(table)
    if int(current_version) < int(version):  # Table need update or not extis
        self.db.log.info("Table %s outdated...version: %s need: %s, rebuilding..." % (table, current_version, version))
        self.createTable(table, cols)
        if indexes:
            self.createIndexes(table, indexes)
        self.execute(
            "INSERT OR REPLACE INTO keyvalue ?",
            {"json_id": 0, "key": "table.%s.version" % table, "value": version}
        )
        return True
    else:  # Not changed
        return False

可以看到,当同名数据表已经存在时,ZeroNet 会判断两者的 version 大小,并决定是否要重建数据表。而对于 json 表而言,这个值或者是 version,或者是自行定义 json 表时设置的 schema_changed。因此,当自行定义了 json 表,且 schema_changed 大于 version 时,自行设置的表结构将会生效,但插入行时仍然将依照 version 设置的行为;相反,若设置的 schema_changed 小于或等于 version自行定义的 json 表结构将不会生效

……依稀记得一开始设置 json 表结构时总是无效,改过几次之后莫名其妙就变得正常了,原来是这样的原理(;´Д`)

keyvalue 表

关于 keyvalue 表和 to_keyvalue 键,文档是这样记述的:

"to_keyvalue": ["next_message_id", "next_topic_id"]
# Load data.json[next_topic_id] to keyvalues table
# (key: next_message_id, value: data.json[next_message_id] value)

……嗯,只有这两句话,而且表名还写错了。总之,看一看实现吧:

# src/Db/Db.py#L168
# Check keyvalue table
changed = cur.needTable("keyvalue", [
    ["keyvalue_id", "INTEGER PRIMARY KEY AUTOINCREMENT"],
    ["key", "TEXT"],
    ["value", "INTEGER"],
    ["json_id", "INTEGER"],
], [
    "CREATE UNIQUE INDEX key_id ON keyvalue(json_id, key)"
], version=self.schema["version"])
if changed:
    changed_tables.append("keyvalue")
# src/Db/Db.py#L273
if dbmap.get("to_keyvalue"):
    # Get current values
    res = cur.execute("SELECT * FROM keyvalue WHERE json_id = ?", (json_row["json_id"],))
    current_keyvalue = {}
    current_keyvalue_id = {}
    for row in res:
        current_keyvalue[row["key"]] = row["value"]
        current_keyvalue_id[row["key"]] = row["keyvalue_id"]

    for key in dbmap["to_keyvalue"]:
        if key not in current_keyvalue:  # Keyvalue not exist yet in the db
            cur.execute(
                "INSERT INTO keyvalue ?",
                {"key": key, "value": data.get(key), "json_id": json_row["json_id"]}
            )
        elif data.get(key) != current_keyvalue[key]:  # Keyvalue different value
            cur.execute(
                "UPDATE keyvalue SET value = ? WHERE keyvalue_id = ?",
                (data.get(key), current_keyvalue_id[key])
            )

首先,和之前的 json 表同样,keyvalue 表的结构版本也等同于 version默认情况下,value 的类型为整数。如果需要 (json_id, key) 之外的索引,或者整型之外的 value 值,可以通过设置大于 versionschema_changed 来覆盖表结构。其次,对于每个 JSON 文件,keyvalue 表中的键是唯一的,且只能等同于文件中的键名。

to_table 的详细规则

关于 to_table 的详细规则,文档是这样记述的:

{
  "node": "comment_votes", # Reading data.json[comment_votes] key value
  "table": "comment_vote", # Feeding data to comment_vote table
  "key_col": "comment_hash",
    # data.json[comment_votes] is a simple dict, the keys of the
    # dict are loaded to comment_vote table comment_hash column
  "val_col": "vote"
    # The data.json[comment_votes] dict values loaded to comment_vote table vote column
}
{
  "node": "includes",
  "table": "user",
  "key_col": "path",
  "import_cols": ["user_id", "user_name", "max_size", "added"],
    # Only import these columns to user table
  "replaces": {
    "path": {"content.json": "data.json"}
      # Replace content.json to data.json in the
      # value of path column (required for joining)
  }
}

嗯……意外地好像说得很清楚?不过为了放心,还是看一下实现吧:

# src/Db/Db.py#L333
if key_col:  # Map as dict
    for key, val in data[node].iteritems():
        if val_col:  # Single value
            cur.execute(
                "INSERT OR REPLACE INTO %s ?" % table_name,
                {key_col: key, val_col: val, "json_id": json_row["json_id"]}
            )
        else:  # Multi value
            if isinstance(val, dict):  # Single row
                row = val
                if import_cols:
                    row = {key: row[key] for key in row if key in import_cols}  # Filter row by import_cols
                row[key_col] = key
                # Replace in value if necessary
                if replaces:
                    for replace_key, replace in replaces.iteritems():
                        if replace_key in row:
                            for replace_from, replace_to in replace.iteritems():
                                row[replace_key] = row[replace_key].replace(replace_from, replace_to)

                row["json_id"] = json_row["json_id"]
                cur.execute("INSERT OR REPLACE INTO %s ?" % table_name, row)
            else:  # Multi row
                for row in val:
                    row[key_col] = key
                    row["json_id"] = json_row["json_id"]
                    cur.execute("INSERT OR REPLACE INTO %s ?" % table_name, row)
else:  # Map as list
    for row in data[node]:
        row["json_id"] = json_row["json_id"]
        if import_cols:
            row = {key: row[key] for key in row if key in import_cols}  # Filter row by import_cols
        cur.execute("INSERT OR REPLACE INTO %s ?" % table_name, row)

(´゚д゚`)

嗯……怎么描述呢,这个情况……

  • 规则中的 node 必须是 JSON 根对象下第一级的键名。
  • 规则中的 import_cols 当且仅当满足如下情况之一时有效:
    • 未设置 key_col
    • 设置了 key_col,且满足如下全部情况:
      • 未设置 val_col
      • JSON 文件中名称等于 node 的值的键的值类型为 JSON Object。
  • 规则中的 replaces 当且仅当满足如下情况时有效:
    • 设置了 key_col
    • 未设置 val_col
    • JSON 文件中名称等于 node 的值的键的值类型为 JSON Object。

大体上而言,只有和代码样例完全相同的用法才能生效。一定要说的话,后两项这样的情况与其说是有意的设计,不如说更像是为了特定用途临时增加功能的产物。ZeroNet 开发者在写下这段代码时是如何思考的,自然无从得知,不过对于希望使用 import_colsreplaces 的用户而言,这应该会成为一个不小的麻烦吧。

就是这样!


附言:也许你已经注意到了,不过表结构的 index 数组中其实是可以填入任意 SQL 语句,使它在创建新表后执行的。可以用来做一些有趣的事情也说不定!

一般来说,有了十个以上的节点,就能愉快打开网站了,但有时候也会出现有十几个节点,但仍然打不开网站的情况。大概猜测一下原因:


ZeroNet上的节点有两大类,一类是“主动模式”,一类是“被动模式”。打开15441端口,并且有公网IP的节点是主动模式,其他节点是被动模式。主动模式节点和主动模式节点之间可以直接连接,主动模式节点和被动模式节点之间也可以直接连接,被动模式节点和被动模式节点之间不能直接连接。 所以如果一个网站的在线节点都是被动模式,那么其他被动模式的节点是打不开这个网站的。另外如果和所有主动模式节点之间的连接都不通畅,也会出现同样的问题。

转帖请带上原贴网址: https://www.zerogate.tk/leafok.bit/?Post:18 非常感谢!