You are reading content from Scuttlebutt
Feed of @David

Language revitalizationist, learner, teacher, helper, coder.

(he/him)

@David
Followed @catherine leigh schmidt
@David

Hi scuttlefriends! I'm looking for some #scuttlebot / #ssb-dev help. I thought I'd try my hand at putting together a simple SSB client—that's part of the hope, right, that different people can create their own clients?

I've been experimenting with scuttlebot and figuring out what I have to do to make it work, vs. what it already handles for me. Below I've reproduced a test I've written (using Jest) to help me understand replication, modeled off of scuttlebot's realtime.js test.

This test tells me that replication doesn't happen until one sbot connects to another via its address. However, it's not clear to me how I would get that address in real life.

Crawling around the source led me to the local plugin, which looks like after a 1000ms wait, it should cause local peers to show up in sbot.gossip.peers(). I was able to get that to happen in my test...but only by including bob's address in the seeds array for alice's sbot. But that's a catch-22, because I need bob's address in order to make sbot.gossip.peers() give me bob's address...

(Also, including the seeds option causes replicated messages to no longer show up in my "connected sbots replicate to each other" test, and I haven't been able to figure out why.)

Some other riddles I've encountered:

  1. I don't seem to be tearing down my test sbots properly. The test hangs, and Jest warns that "there are asynchronous operations that weren't stopped in your tests". I've tried closing the two sbots in a couple of ways, including the way it seems that the realtime.js test does it. (I'm also successfully starting and stopping test sbots in other tests, although this is the only test that runs two sbots at the same time.)

  2. When the test runs, I get multiple warnings from ssb-friends/legacy.js, saying ssb-friends: stream legacy api used. I'm not sure what I'm doing to cause that error.

Any guidance you could give about these riddles, or how I should discover sbot addresses to connect to, would be much appreciated! Thanks for reading.

Test below:

const { promisify } = require('util')
const pull = require('pull-stream')
const ssbKeys = require('ssb-keys')
const getPort = require('get-port')

var createSbot = require('scuttlebot')
  .use(require('scuttlebot/plugins/master'))
  .use(require('scuttlebot/plugins/replicate'))
  .use(require('scuttlebot/plugins/gossip'))
  .use(require('scuttlebot/plugins/local'))
  .use(require('ssb-ebt'))
  .use(require('ssb-friends'))

describe('Multiple sbots', () => {
  let alice
  let bob

  beforeEach(() =>
    getPort()
      .then(port => {
        bob = createSbot({
          temp: 'test-bob-' + Math.round(Math.random()*10000),
          port: port,
          host: 'localhost',
          replicate: {legacy: false},
          keys: ssbKeys.generate()
        })
      })
      .then(getPort)
      .then(port => {
        alice = createSbot({
          temp: 'test-alice-' + Math.round(Math.random()*10000),
          port: port,
          host: 'localhost',

          // I don't know what the `seeds` option is for, but
          // scuttlebot/test/realtime.js includes it in alice's opts.
          // Including it causes my can-discover-peers-via-gossip.peers
          // test to pass, but also causes my can-see-replicated-messages
          // test to fail.

          // seeds: [bob.getAddress()],

          replicate: {legacy: false},
          keys: ssbKeys.generate()
        })
      })
      .then(waitLongEnoughForLocalPeerBroadcasts)
      .then(() => 
        promisify(bob.publish)({
          type: 'contact', contact: alice.id, following: true
        }).then(() => promisify(alice.publish)({
          type: 'contact', contact: bob.id, following: true
        }))
      )
  )

  describe('when two sbots are not connected', () => {
    it('does not replicate between them', (done) => {
      promisify(alice.publish)({
        type: 'test',
        value: 'Hello!'
      })
      .then(waitLongEnoughForReplicationToHappen)
      .then(() => {
        pull(
          bob.createUserStream({id: alice.id, keys: false}),
          pull.collect((err, messages) => {
            expect(messages).toEqual([])
            done()
          })
        )
      })
    })

    // Fails! (got [], expected not [])
    it('can find each other via gossip.peers', (done) => {
      alice.gossip.peers((err, peers) => {
        if(err) throw err

        expect(peers).not.toEqual([])
        done()
      })
    })
  })

  describe('when two sbots are connected', () => {
    beforeEach(() => promisify(alice.connect)(bob.getAddress()))

    it('replicates between them', (done) => {
      promisify(alice.publish)({
        type: 'test',
        value: 'Hello!'
      })
      .then(waitLongEnoughForReplicationToHappen)
      .then(() => {
        pull(
          bob.createUserStream({id: alice.id, keys: false}),
          pull.collect((err, messages) => {
            expect(
              messages
                .filter(message => message.content.type === 'test')
                .map(message => message.content.value)
            ).toContain('Hello!')
            done()
          })
        )
      })
    })
  })

  // After some trial and error, 200ms seems to be the magic
  // threshold for long-enough-for-replication-to-happen.
  const waitLongEnoughForReplicationToHappen = () =>
    new Promise(r => setTimeout(r, 200))

  // scuttlebot/plugins/local broadcasts itself every 1000ms.
  // Wait a little longer than that.
  const waitLongEnoughForLocalPeerBroadcasts = () =>
    new Promise(r => setTimeout(r, 1300))

  afterEach(done => {
    // I've tried this clean up a couple of ways, and none seem to
    // work properly; the test hangs after completion and reports
    // that some asynchronous processes may not have stopped.
    // I've tried:
    //
    // 1. promisifying the close functions using node's promisify
    //    utility and returning the promise (so that the test harness
    //    will wait for the promise to resolve before ending the test).
    //
    // 2. calling the close functions with (true), as
    //    scuttlebot/test/realtime.js does.
    //
    // 3. nesting callbacks, as seen below.
    //
    // Using log statements, I have confirmed that the close functions'
    // callbacks _are_ being invoked, so I don't know what's making the
    // test hang.
    //
    alice.close(err => {
      bob.close(err => {
        done()
      })
    })
  })
})
@David
Re: %58kc+ebun

Follow up for @Zach!: were you able to keep up with your Gaelic at all? There's an active effort to keep that language strong as well!

@David
Re: %58kc+ebun

Thanks @Zach! Feelings like that, where a language lent me a new way of thinking or helped me see something differently, are a lot of what got me involved with endangered language work in the first place.

When I was first getting fluent in #chinuk-wawa , I noticed that I had an easier time making certain decisions if I switched into it, even just in my head. In English I might say something like:

"Oh man, I really want to go out with y'all tonight, but I need to finish this thing I'm working on."

Such a decision is...I think the word I want is laborious. Pulling away from this thing you really want to do this other thing, that you don't want to do even though you need to, is hard.

In wawa, though, there aren't different words for "want" and "need". They're both just tiki, although when you need to tell the difference you might render "need" as dret tiki, where dret is something in the vicinity of very/really/true/straight.

So now you're in the position of saying:

"Oh man, I tiki go out with y'all tonight, but I dret tiki finish this thing I'm working on."

And now the decision is obvious. Given mutually exclusive things that you tiki, you take the thing you tiki more. The outcome of the decision isn't any different, but I noticed that it felt less emotionally taxing when I made it in wawa.

Nice to meet you @bobhaugen ! That was a fun article—it's cool to see something I don't get much opportunity to share with people pop up in public like that.

I wish (like anybody who reads a popular article about something close to their heart) that the article had been a bit more accurate. chinuk wawa is not a pidgin—it is a creole, because, as the article pointed out, children grew up speaking it natively.

See more on the difference here. When a pidgin grows into a creole, it fleshes out in grammatical complexity a great deal, in order to accommodate the needs of its full-time speakers. A pidgin is not a fully developed language, but a creole—and chinuk wawa—absolutely is.

I also bristled at lines like this:

...Powell, who is one of the last fluent speakers of Chinook Wawa...

...Today, some of the last traces of Chinook Wawa can be found in our landscape...

wik ukuk lalang chaku hilu, hayu tilixam ɬaska chaku kəmtəks ukuk lalang, pi hayu tilixam ɬaska wawa ukuk alta. wik dret hayu kakwa pastən wawa, but wik ukuk Powell ukuk kimt'a: man uk ya wawa chinuk wawa. wik ukuk lalang miɬayt kʰapit kʰapa ilaʕi-nim, ka: Grand Ronde tilixam ɬaska wawa ukuk!

Powell may be one of the few, but he is absolutely not one of the last.

It's a hard thing...I'm excited to see the language get some public exposure, but the article talked about it in the past tense, as a disappearing, soon-to-be-forgotten thing, never mentioning the whole groups of people—including many fluent speakers!—devoting huge amounts of time and effort to ensuring that the great-grandparents of the last speakers of chinuk wawa have not even been born yet.

(And it's working! The language is growing, there are more learners all the time!)

Anyway.

I'd love to hear what Gladys thinks about the Same Conversation technique—I'll bet she's used a lot of similar techniques when teaching! I'm glad I could provide an interesting piece of discussion. Please let her know that chinuk wawa is very much alive, and isn't going anywhere!

@David
Followed @Zach
@David
Voted Not long after I first began publishing to secure-scuttlebutt there was a
@David
Unfollowed @Lex Tenebris
@David
Followed @John Colagioia
@David
Voted Computer crashed on the first shot in saying this, so this'll probably be a
@David
Followed @niKDo

Show whole feed
Join Scuttlebutt now