[Distributed SNS] A story about the making Shinjiro Koizumi bot on Nostr.
Tags: sns, nostr, bot, javascript
Published: 2023-12-13 12:46:37

**This is the 14th article of [Nostr (1) Advent Calendar 2023](https://adventar.org/calendars/8794)**
Japanese ver is [here](https://zenn.dev/hikaelis/articles/6f7d34974aaf49)

I made a GPT-4 Shinjiro Koizumi bot on Nostr and X(Twitter).
Links↓
- [Nostr](https://nostx.shino3.net/npub1shnjrjegvfp48heaw43qgsz44f4uffk50pe4cq73xj5fcmd4qydqy3r4l6)
- [X(Twitter)](https://twitter.com/shinjiro_AI_bot)
- [GitHub](https://github.com/hikaelis/nostr_shinjiro_bot_public)

## Introduction
### Who's Shinjiro?
Do you know Shinjiro Koizumi? He is a Japanese statesman, his father is ex-Prime Minister Junichiro Koizumi.

![Image description](https://github.com/hikaelis/hikaelis.github.io/blob/main/images/Shinjiro_Koizumi.jpg?raw=true)
![Image description](https://github.com/hikaelis/hikaelis.github.io/blob/main/images/rekidai-index-89-koizumi.jpg?raw=true)

Mr.Shinjiro Koizumi is kinda Internet meme in Japan. Because his remarks are often so "Interesting". 
Why? That is "Tautology". That means his sentence looks so meaningful but meaningless actually. We call it **Shinjiro-Syntax**.
These are examples. His actual remarks. 

- I don't think Japan should stay the way it is now. That's why I think Japan shouldn't stay the way it is now.
-  Thanks to working from home, I can carry out my official work remotely.
- To the point that I don't seem to be remorseful, I remose it. it's my own problem.
- I heard you were born on your birthday.(This's ogiri)

### What's Nostr?
Nostr is a protol, that's used for next generation distributed SNS. I don't explain about Nostr this time, but I highly recommend it because it's "the Free[^1] and Secure"!
Please check out here↓
- [The Nostr Protocol](https://nostr.how/en/the-protocol)

## Coding 
Pioneers have already lectured details how to write codes for Nostr Protocol on these pages or books, so I just intoduce the ingenuity and stuck points
- [learn-nostr-by-crafting](https://github.com/nostr-jp/learn-nostr-by-crafting)
- [Hello Nostr! Yo Bluesky! 分散SNSの最前線](https://techbookfest.org/product/6quLEm85cpd4TMJR17xnVF?productVariantID=kgmgxRsKgbVruvRd2zV1sp)

### 1. Connection to multiple relays
Now, it seems to be easy to use SimplePool() of nostr-tools to post to multiple relays, but I somehow couldn't work at that time. so I use a simple for statement. 

```js
// Post
if (message != null){
  for (const relayURL of relayURLs) {
    try {
	  await postToRelays(relayURL, metadata);
	} catch (e) {
	  console.log(e);
	  continue;
	  }
	}
  }
```

**I added the "Like" function, but lots of Shinjiro**
When I posted, "Shinjiro" is occasionally mentioned in the timeline. Each time, I used to manually like the post, but it's annoying, so I automated it. (Probably if Shinjiro liked their posts, Everyone would be happy)
This time, I've decided to like all posts if it contains the word "Koizumi" or "Shinjiro".
I retrieved the all  posts from the relays, and filtered them with includes(), and send a Like event (kind7).
When I finally run it, somehow, a lots of of Shinjiro appeared in the Like filed, causing the timeline to buzz.
- Proliferated Shinjiro
{% embed https://damus.io/note1e6vz0ugav0srnd4ywt39sue0lqksse0lfvfkuzlkj3szznhlq9ssxatskg %} 
That's why sending events to multiple relays,  but it is a mystery because it only generates just one event so it is unique...(so the number of likes is normal). 
And then I send like event once an hour, so I tried to reduce duplicates as much as possible by retrieving only posts within an hour.
```js
const likePosts = async (relayUrl, targetWords) => {
  const relay = relayInit(relayUrl);
  relay.on("error", () => {
    console.error("failed to connect");
  });

  await relay.connect();
  const sub = relay.sub([{ kinds: [1] }]);
  let num = 0;
  sub.on("event", (ev) => {
    try {
      const currentTime = currUnixtime(); // Now UNIX TIME
      const oneHourAgo = currentTime - 3600; // UNIX TIME 1 hour ago

      // Like only posts which includes target words and within an hour
      if ((ev.created_at > oneHourAgo) && (ev.content.includes(targetWords[0]) || ev.content.includes(targetWords[1]))) {
        const reaction = composeReaction(ev);
        publishToRelay(relay, reaction);
        num++;
      }
    } catch (err) {
      console.error(err);
    }
  });
  return num;
};

```

### 2. Can't I use OpenAI's API?
When I tried to use the JavaScript OpenAI library, I got an error message and had trouble. It seems that the usage has changed with the newer version. I introduced it in another article.
- [How to use OpenAI API with JavaScript](https://dev.to/hikaelis/how-to-use-openai-api-with-javascript-1o56)

### 3. Botting with GitHub Actions
As it is, I am running the script manually, so it is not a bot. I wanted to run it automatically, so at first I rented a free server (GoogleComputeEngine) and tried to run it all the time, but the SSH connection was very unstable.
I thought, "If I just wanna run it periodically (once an hour), Why not use GitHub Actions? I decided to do so.
I found out that Actions can also use cron-like code.
This time, I just need to make a node environment and run main.js, so I asked ChatGPT to write a workflow configuration file.
like this (uncomment out as needed)

```yml
name: automate
on:
  # schedule:
  #   - cron: '0 * * * *'
  # push:
  #   branches: [ '**' ]

permissions:
  contents: read
  issues: read
  checks: write
  pull-requests: write

jobs:
  post_and_like:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        node-version: [18.x]
    steps:
      - uses: actions/checkout@v3

      - name: Use Node.js ${{ matrix.node-version }}
        uses: actions/setup-node@v3
        with:
          node-version: ${{ matrix.node-version }}
          cache: npm
          cache-dependency-path: ./package-lock.json

      - name: Install Dependencies
        run: npm install
      
      - name: Post and Like
        run: node ./main.js
        timeout-minutes: 1
```

Now, every hour around 0:00 (the schedule of Actions isn't precise), Shinjiro will post and like!
Also, the scheduled execution of Actions will be automatically stopped if there is no changes in the repository for 60 days, so I also made workflow for automatic commit once a day.

```yml
name: auto-commit

on:
  workflow_dispatch:

  # schedule:
  #   - cron:  "0 15 * * *" # Carry out every 24:00 in Japan

jobs:
  build:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v3

      - name: Set current datetime as env variable
        env:
          TZ: 'Asia/Tokyo' 
        run: echo "CURRENT_DATETIME=$(date +'%Y-%m-%d %H:%M:%S')" >> $GITHUB_ENV

      - name: Commit
        run: |
          git config --global user.email ${{ secrets.USER_EMAIL }}
          git config --global user.name ${{ secrets.USER_NAME }}
          echo -e ${{ env.CURRENT_DATETIME }} >> commit_log.txt
          git add commit_log.txt
          git commit -m "[add] 自動コミット:${{ env.CURRENT_DATETIME }}"
          git push origin main     

```

## Result

I got a crazy buzz on X(Twitter).
I immediately started running it on Nostr, and it became a hot topic there.
I was ranked No. 1 several times in Nostr's Buzz Word ranking, which satisfied my need for approval.
Then one day, Mr.mattn introduced me on X, and I got a lot of buzz (1.2 million impressions ❗️).
I heard that it was also flowing to X of my friend...
{% embed https://x.com/mattn_jp/status/1726762502225342470 %}

> I have no choice but to get on this big wave!

So, I rapidly got the API of X, and started on X as well. (The number of followers has not grown much since then...)
- Shinjiro Bot on X
{% embed https://x.com/shinjiro_ai_bot/status/1726901828800172169) %}

But... Shinjiro is definitey popular![^2] Thanks, Shinjiro!!


[^1]: Don't become a slave of Freedom (by Attack on Titan)
[^2]: Not including political intentions