├── .gitignore ├── screen.png ├── package.json ├── README.md └── app.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .env 3 | -------------------------------------------------------------------------------- /screen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unwriter/_opreturn/HEAD/screen.png -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "_opreturn", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "app.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": { 12 | "dotenv": "^5.0.1", 13 | "eventsource": "^1.0.7", 14 | "twitter": "^1.7.1" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # _opreturn 2 | 3 | [@_opreturn](https://twitter.com/_opreturn) 4 | 5 | A portal into the wonderland of Bitcoin powered censorship resistant social media. Powered by http://bitsocket.org 6 | 7 | ![img](./screen.png) 8 | 9 | # What 10 | 11 | `_opreturn` is a Twitter bot that utilizes [Bitsocket](https://bitsocket.org) to monitor realtime events from Bitcoin cash powered social media protocols such as memo.cash and matter.cash and replicate them to Twitter using Twitter's API. 12 | 13 | We're turning Twitter into an application-specific Bitcoin sidechain. 14 | 15 | # How 16 | 17 | [Bitsocket](https://bitsocket.org) lets you listen to all realtime transactions from Bitcoin Cash blockchain through SSE (Server Sent Events) 18 | 19 | You can listen to whichever pattern you want by writing a Turing complete query language called [Bitquery](https://docs.bitdb.network/docs/query_v3) 20 | 21 | # Why 22 | 23 | There can be many powerful use cases, but it all comes down to the fact that for the first time we have a portal from uncensorable media into censorable media. Use your imagination. 24 | 25 | For example, if your Twitter account gets banned, shadowbanned, or hijacked, you can still express yourself outside of Twitter through censorship resistant social media such as memo.cash and matter.cash, and they will be brought back into Twitter-verse and heard by others who care (who follow this account). 26 | 27 | # Features 28 | 29 | 1. By default it replicates all regular posts ("6d02" for memo.cash and "9d01" for matter.cash). 30 | 2. You can also trigger replication by simply mentioning `@_opreturn` in your message 31 | 32 | # Feature Requests 33 | 34 | I think there can be many additional cool features that can be implemented. Feel free to [open an issue to start a discussion](https://github.com/unwriter/_opreturn/issues/new). 35 | -------------------------------------------------------------------------------- /app.js: -------------------------------------------------------------------------------- 1 | require('dotenv').config() 2 | const EventSource = require('eventsource') 3 | const Twitter = require('twitter'); 4 | 5 | // Twitter 6 | var client = new Twitter({ 7 | consumer_key: process.env.consumer_key, 8 | consumer_secret: process.env.consumer_secret, 9 | access_token_key: process.env.access_token_key, 10 | access_token_secret: process.env.access_token_secret 11 | }); 12 | var send = function(msg) { 13 | console.log("Posting", msg) 14 | client.post('statuses/update', {status: msg}, function(error, tweet, response) { 15 | if (!error) { 16 | console.log(tweet); 17 | } 18 | }); 19 | } 20 | module.exports = { 21 | send: send 22 | } 23 | var query = { 24 | "v": 3, 25 | "q": { 26 | "find": { "out.b0": { "op": 106 } } 27 | }, 28 | "r": { 29 | "f": "{ tx: .[0].tx.h, out: (.[0].out[] | select(.b0.op? == 106)) }" 30 | } 31 | } 32 | 33 | // Bitsocket 34 | var b64 = Buffer.from(JSON.stringify(query)).toString("base64") 35 | var bs = new EventSource('https://bitsocket.org/s/'+b64) 36 | bs.onmessage = function(e) { 37 | let event = JSON.parse(e.data) 38 | if (event.type === 'mempool') { 39 | let tx = event.data.tx 40 | let out = event.data.out 41 | try { 42 | if (["6d02", "6d03", "6d0c", "6d10"].includes(out.h1)) { 43 | // Memo.cash 44 | if (out.h1 === "6d02") { 45 | // regular memo. post all 46 | send(`${out.s2} https://memo.cash/post/${tx}`) 47 | } else if (out.h1 === "6d03") { 48 | // reply 49 | if (/@_opreturn/.test(out.s3)) { 50 | send(`(Comment) ${out.s3} https://memo.cash/post/${tx}`) 51 | } 52 | } else if (out.h1 === "6d0c") { 53 | // topic 54 | if (/@_opreturn/.test(out.s3)) { 55 | send(`(Topic: ${out.s2}) ${out.s3} https://memo.cash/post/${tx}`) 56 | } 57 | } else if (out.h1 === '6d10') { 58 | // poll 59 | if (/@_opreturn/.test(out.s4)) { 60 | send(`(Poll) ${out.s4} https://memo.cash/post/${tx}`) 61 | } 62 | } 63 | } else if (out.h1 === "9d01") { 64 | // Matter.cash 65 | send(`(Longform) ${out.s4} https://www.mttr.app/p/${tx}`) 66 | } 67 | } catch (e) { 68 | console.log(e, event.data) 69 | } 70 | } 71 | } 72 | --------------------------------------------------------------------------------