【操作指南】100行代码实现cmd-line聊天应用程序

【操作指南】100行代码实现cmd-line聊天应用程序

本文由IPFS原力区收集译制,版权所属原作者

 下面是为 Nodejs 构建 offline-first 聊天应用程序的最快方法 

【操作指南】100行代码实现cmd-line聊天应用程序

William Navarro Unsplash上的照片 

引用offlinefirst.org的话说,我们生活在一个断开连接、靠电池供电的世界,但我们的技术和最佳实践是过去一直连接并稳定供电的时代遗留下来的。Textile,我们发现一种新的应用程序开发前进方向,它包含现代的、分散的web技术,同时促进以用户为中心的数据控制。但是分散式的应用程序开发仍然处于起步阶段,并且开始时非常困难。这就是Textile不断增长的开发工具套件发挥作用的地方——使得开发人员可以更容易地构建分散的、离线优先的、保护隐私的应用程序,这些应用程序使用的工具与他们期望从更传统的集中框架中获得的工具类型相同。

为了展示使用Textile构建有用的应用程序是多么容易,今天的文章将介绍如何构建一个极简主义的cmd-line聊天应用程序,它是完全分散的,支持离线消息传递、对等发现等等。我们将用不到100行“客户端”代码来完成。就“hello world”示例应用程序而言,这个应用程序实际上非常有用。听起来好得令人难以置信?让我们来看看? !

对于没有耐心的人,这里是完整的代码。(原文链接查看

开始

首先,我们要在分散式开发工具的Textile生态系统上构建我们的应用。特别是,我们将利用Textile desktop tray应用程序运行Textile/IPFS节点、js-http-client库来与上述节点进行接口,以及其他几个Nodejs库来帮助清理基于终端的用户界面。因此,让我们从下载并安装最新的桌面应用程序开始。对于本演示的目的,您还可以在创建Textile帐户时创建一个新的帐户。应用程序的登录屏幕应该会帮助你开始。但在你深入了解之前,我强烈建议你看看Tour of Textile。我们将在这篇文章中引用该文档中的一些部分,另外它还解释了什么是Textile wallet帐户,为什么要创建一个呢,以及如何与之互动……

设置

现在您是Textile技术方面的专家,已经下载并安装了tray应用程序,让我们开始构建。首先,我们将创建一个最小的Nodejs项目,并安装一些初始依赖:

  • mkdir txtl
  • cd txtl
  • yarn init

按照提示生成packagejson文件,我的是这样的:

  • {
  • “name”: “txtl”,
  • “version”: “1.0.0”,
  • “description”: “A simple cli chat app using Textile”,
  • “main”: “index.js”,
  • “author”: “Textile”,
  • “license”: “MIT”
  • }
如果您想对这个项目进行版本控制,您还可以git initcurl https://www.gitignore.io/api/node > .gitignore来初始化一个带有有用的.gitignore文件的repo。

依赖关系

接下来,让我们添加一些依赖项。我们前面提到js-http-client(这就是我们如何将访问Textile托盘程序的API),让我们抓住这一个还有三个附加的库,简化cmd-line应用Nodejs (cac -构建我们cmd-line接口,chalk——终端串样式和颜色,和node-emoji——因为我们需要emojis):

  • yarn add @textile/js-http-client cac chalk node-emoji

好了,有了这些东西,让我们想想我们要做什么。我们基本上要模拟库来与上述节点进行接口,以及其他几个Nodejs库来帮助清理基于终端的用户界面。因此,让我们从下载并安装最新的桌面应用程序开始。对于本演示的目的,您还可以在创建Textile帐户时创建一个新的帐户。应用程序的登录屏幕应该会帮助你开始。但在你深入了解之前,我强烈建议你看看Tour of TextileStart a chat部分,但在我们的示例中,一个(或两个)对等节点将使用自定义Nodejs聊天接口。如果你看看它的一部分,你会看到我们有一个非常简单的cmd-line聊天应用程序,你开始与Textile聊天在stdin和接收输入,并将输出写入标准输出,和一些不错的颜色,使它更有趣的事情。很简单对吧?我们开始吧。

 设置界面

首先,我们将创建cmd-line接口,然后添加实际的基于文本的代码,然后添加一些颜色和调整。我们将在此过程中添加所需的导入,这样您就可以看到每个更改来自何处。因此,首先在主项目文件夹中创建index.js文件(我使用VSCode,但您可以坚持使用任何编辑器):

  • #!/usr/bin/env node
  • const cli = require(‘cac’)(‘txtl’)
  • const { log } = console
  • // Create ‘default’ chat command
  • cli.command(”, ‘Starts an interactive chat session in a thread.’)
  • .action((opts) => {
  • log(‘do something’)
  • })
  • .option(‘–thread [thread]’, ‘Thread ID. Omit to use the \’default\’ thread.’, {
  • default: ‘default’,
  • type: [String]
  • })
  • // Display help message when `-h` or `–help` appears
  • cli.help()
  • // Display version number when `-v` or `–version` appears
  • cli.version(‘1.0.0’)
  • // Parse stdin
  • cli.parse()

这里没什么特别的,我们只是从cac文档中复制粘贴。我们正在模仿go-textile cmd-line客户机的接口,所以这里也添加了——thread选项。接下来,我们将使用readline模块向cmd-line工具添加一些交互性。以下是我们需要做的更新:

  • diff –git a/index.js b/index.js
  • index 11b46ac..4cf1473 100644
  • — a/index.js
  • b/index.js
  • @@ -1,6 +1,7 @@
  • #!/usr/bin/env node
  • const cli = require(‘cac’)(‘txtl’)
  • var readline = require(‘readline’)
  • const { log } = console
  • @@ -8,6 +9,19 @@ const { log } = console
  • cli.command(”, ‘Starts an interactive chat session in a thread.’)
  • .action((opts) => {
  • log(‘do something’)
  • // Specify our readline interface
  • const rl = readline.createInterface({
  • input: process.stdin,
  • output: process.stdout,
  • terminal: false
  • })
  • // Run callback for each input on stdin
  • rl.on(‘line’, (line) => {
  • if (line !== ”) {
  • log(line)
  • rl.prompt()
  • }
  • })
  • })
  • .option(‘–thread [thread]’, ‘Thread ID. Omit to use the \’default\’ thread.’, {
  • default: ‘default’,

我们只需指定readline接口,然后在每次输入新行时将输入回显到终端。还不是很有用…所以让我们添加一些Textile功能,使其有用。我们假设您已经有一个go-textile或桌面托盘应用程序运行,因此我们将直接与该应用程序交互。

与Textile交互

  • diff –git a/index.js b/index.js
  • index 4cf1473..598b1d1 100644
  • — a/index.js
  • b/index.js
  • @@ -1,6 +1,7 @@
  • #!/usr/bin/env node
  • const cli = require(‘cac’)(‘txtl’)
  • const textile = require(‘@textile/js-http-client’).default
  • var readline = require(‘readline’)
  • const { log } = console
  • @@ -8,7 +9,6 @@ const { log } = console
  • // Create ‘default’ chat command
  • cli.command(”, ‘Starts an interactive chat session in a thread.’)
  • .action((opts) => {
  • log(‘do something’)
  • // Specify our readline interface
  • const rl = readline.createInterface({
  • input: process.stdin,
  • @@ -18,10 +18,22 @@ cli.command(”, ‘Starts an interactive chat session in a thread.’)
  • // Run callback for each input on stdin
  • rl.on(‘line’, (line) => {
  • if (line !== ”) {
  • log(line)
  • rl.prompt()
  • textile.messages.add(opts.thread, line)
  • .then(() => { rl.prompt() })
  • }
  • })
  • // Get our ‘local’ profile info…
  • textile.profile.get()
  • .then((peer) => {
  • // … and then create a custom prompt
  • rl.setPrompt(peer.name || peer.address.slice(7) + ‘\t’)
  • rl.prompt() // Display prompt to get started
  • // Only subscribe to text events on the specified thread
  • textile.subscribe.stream([‘TEXT’], opts.thread)
  • .then((stream) => {
  • log(‘do something’)
  • })
  • })
  • })
  • .option(‘–thread [thread]’, ‘Thread ID. Omit to use the \’default\’ thread.’, {
  • default: ‘default’,
好了,这部分还没有完全完成,接下来是
  • 在第9行,我们从textile库导入了默认的textile实例。这个实例假设您正在http://127.0.0.1:40600上运行Textile REST API(默认情况下是这个API)。如果在其他端口上运行,则必须导入Textile(类),并使用const Textile = new Textile({url:…,port:…,version: 0})创建一个新实例。

  • 在第27行中,我们已经指定,对于来自终端的每个新输入,我们将把消息发送到指定的线程,然后在下一行打印出一个漂亮的提示符。

  • 31-42行只是获取我们的本地概要文件,并使用我们的显示名称(如果它存在,否则是地址的第一部分)作为输入提示符。然后,我们订阅指定线程上的所有文本事件,并且……到目前为止什么都没有做。 

订阅更新

现在我们已经基本设置好了,让我们对订阅API做一些实际操作,这样当新的聊天消息添加到线程中时,我们就可以得到通知。

  • diff –git a/index.js b/index.js
  • index 598b1d1..963d96f 100644
  • — a/index.js
  • b/index.js
  • @@ -31,7 +31,31 @@ cli.command(”, ‘Starts an interactive chat session in a thread.’)
  • // Only subscribe to text events on the specified thread
  • textile.subscribe.stream([‘TEXT’], opts.thread)
  • .then((stream) => {
  • log(‘do something’)
  • // All js-http-client stream endpoints return a ReadableSream
  • const reader = stream.getReader()
  • const read = (result) => {
  • if (result.done) {
  • return
  • }
  • // Extract the text update and display it nicely…
  • try {
  • const item = result.value.payload
  • const name = item.user.name || item.user.address.slice(7)
  • if (item.user.address !== peer.address) {
  • readline.clearLine(process.stdout, 0)
  • readline.cursorTo(process.stdout, 0)
  • log(name + ‘\t’ + item.body)
  • rl.prompt()
  • }
  • } catch (err) {
  • reader.cancel(undefined)
  • return
  • }
  • // Keep reading from the stream
  • reader.read().then(read)
  • }
  • // Start reading from the stream
  • reader.read().then(read)
  • })
  • })
  • })

这是迄今为止最复杂的一步,请注意,我们没有做任何错误检查或类似的事情来保持事情的简单。现在,注意所有的js-http-client流端点都返回一个ReadableStream(在Nodejs和浏览器中,多亏了一些同态的魔力!)这个流可以通过管道连接到转换流或任何可写流,并且您可以获取一个阅读器来从流中读取更新,这就是我们在这里所做的。在上面的示例中(与MDN web docs示例非常相似),我们只是递归地调用read,直到流done(只要您的对等程序运行,流就应该保持打开状态)。任何时候我们有一个新的更新,我们都会抓取它,找出添加它的用户,如果不是我们,我们就清除当前行,添加新的消息,并记录一个新的提示。简单!

让它Purdy……

上面的例子已经准备好了,但是如果我们真的想要模拟(并构建)go-textile版本,我们需要一些最后的润色:

  • diff –git a/index.js b/index.js
  • index 963d96f..6e7bfc1 100644
  • a/index.js
  • b/index.js
  • @@ -3,6 +3,8 @@
  • const cli = require(‘cac’)(‘txtl’)
  • const textile = require(‘@textile/js-http-client’).default
  • var readline = require(‘readline’)
  • const chalk = require(‘chalk’)
  • const { emojify } = require(‘node-emoji’)
  • const { log } = console
  • @@ -18,7 +20,7 @@ cli.command(”, ‘Starts an interactive chat session in a thread.’)
  • // Run callback for each input on stdin
  • rl.on(‘line’, (line) => {
  • if (line !== ”) {
  • textile.messages.add(opts.thread, line)
  • textile.messages.add(opts.thread, emojify(line))
  • .then(() => { rl.prompt() })
  • }
  • })
  • @@ -26,7 +28,7 @@ cli.command(”, ‘Starts an interactive chat session in a thread.’)
  • textile.profile.get()
  • .then((peer) => {
  • // … and then create a custom prompt
  • rl.setPrompt(peer.name || peer.address.slice(7) + ‘\t’)
  • rl.setPrompt(chalk.green(peer.name || peer.address.slice(7)) + ‘\t’)
  • rl.prompt() // Display prompt to get started
  • // Only subscribe to text events on the specified thread
  • textile.subscribe.stream([‘TEXT’], opts.thread)
  • @@ -44,7 +46,7 @@ cli.command(”, ‘Starts an interactive chat session in a thread.’)
  • if (item.user.address !== peer.address) {
  • readline.clearLine(process.stdout, 0)
  • readline.cursorTo(process.stdout, 0)
  • log(name + ‘\t’ + item.body)
  • log(chalk.cyan(name) + ‘\t’ + chalk.grey(item.body))
  • rl.prompt()
  • }
  • } catch (err) {

现在我们有了表情符号功能和一些漂亮的颜色来改善我们的聊天体验。您现在应该能够运行这个示例了。因此,在“Tour of Textile”中回到 Start a chat的示例,在启动默认聊天会话(textile chat—thread=12D3K…)的步骤中,使用新的自定义聊天应用程序/index.js—thread=12D3K…(运行chmod +x .index.js之后)。

想要给你新的cmd-line应用程序添加更多的功能吗?将以下内容添加到包中。指定二进制cms -line工具的json文件:

  • "bin": {
  • "txtl": "index.js"
  • }

现在当你运行像yarn add -g txtl这样的东西时,你将拥有一个方便时髦的cmd在线聊天工具!等等,还有呢,想要在打字稿中完成整个过程吗?容易。

超级打印稿

首先添加一些devDependencies

  • yarn add --dev typescript @types/node @types/node-emoji

然后,一旦安装完成,就使用tsconfig初始化tsconfig.json文件:

  • yarn tsc --init

您可能可以保留它的默认值,但我建议至少启用declarationsourceMap,并将outDir设置为“./dist”

现在写一些打字稿。几乎不需要做任何更改,但是这里有一个完整工作的typescript版本的要点,其中有一些额外的检查。

现在,只需运行yarn tsc,就可以得到新生成的commonjs风格的Javascript代码,以及(存根)声明文件。如果您真的想发布这个项目,您可能想要更新bin条目,并向包中添加某种prepare package.json文件,但还是将它作为练习吧。 

离线怎么样

我们还没有真正提到离线的第一部分,首先,为什么offline first?有很多重要的原因,特别是世界上大多数日常使用的web仍然是由移动设备、带宽限制和电池驱动的交互驱动的。但作为一个第一手的例子,写这篇文章的时候,正在世界上互联网极不完善的地方远程办公。Medium web-ui实际上是不可用的,所以用VSCode编写这个文件,并最终将文件上传到Medium以便发布。不是最理想的。聊天应用程序和其他协作工具更受这个问题的困扰。如果和你聊天的人不在网上呢?或者你的网络中断了谈话?如果您都连接到相同的本地网状网络,但没有外部Internet访问怎么办?难道你就不能和别人聊天吗?

这就是离线首先发挥作用的地方,我的本地Textile(感谢IPFS)像冠军一样为我处理这个问题。如果我在离线时正在写一条消息,我的本地对等点将把消息添加到我的本地IPFS实例中,下一次我在线时,它将向我的对等点声明它(就UX而言,它感觉就像消息被“发送”了)。更棒的是,如果我的同事已经在咖啡馆注册了,它就会把我的信息发送到我同事的收件箱,这样他们下次上网时就能收到我的信息。与此同时,任何等待我的消息都将被自动获取,随时可以阅读和交互。对于聊天、照片和文件这样的事情,这可能意味着与朋友和家人保持联系,即使你的互联网连接充其量是断断续续的。 

最好的部分?上面的场景是以完全分散的方式发生的。不需要集中式服务器(即使咖啡馆也只是连接到分散式IPFS网络的额外对等点)。这甚至适用于无法接入更广泛互联网的地区。通过使用Textile作为聊天的“后端”,您可以“免费”获得所有这些离线优先的功能。

-全文完-

本文由IPFS原力区编译,原文链接:

https://medium.com/textileio/decentralized-offline-first-cmd-line-chat-app-in-100-lines-of-code-43ed71a70950

 
【操作指南】100行代码实现cmd-line聊天应用程序

【IPFS原力区】

Slogan:让存储更安全、更高效、更开放、更经济
 
价值观:价值 共建 共享 荣耀
总部位于上海,聚集基于分布式网络&存储的众多技术大咖和爱好者,深耕基于 IPFS 的商业生态建设和社区发展。
每周二举办“分布式存储网络”主题沙龙,聚集了众多技术大咖和 IPFS 爱好者,通过持续输出全面、精细、优质的IPFS咨询和技术支持,将生态中的爱好者转化为IPFS支持者和参与者,共建IPFS生态的健康发展。
【操作指南】100行代码实现cmd-line聊天应用程序

原创文章,作者:IPFSforce,如若转载,请注明出处:http://ipfser.org/2019/07/16/100-lines-of-code-to-implement-the-cmd-line-chat-application/

发表评论

登录后才能评论

联系我们

在线咨询:点击这里给我发消息

邮件:ipfsforce@qq.com

工作时间:周一至周五,9:30-18:30,节假日休息

QR code