Skip to main content

Building a Cwtch Echobot

In this tutorial we will walk through building a simple Cwtch Echobot. A bot that, when messaged, simply responds with the message it was sent.

For completeness, we will build an Echobot in multiple difference Cwtch frameworks to get a feel for the different levels of functionality offered by each library or framework.

Using CwtchBot (Go)

CwtchBot Framework

This tutorial uses the CwtchBot framework.

Start by creating a new Go project, and a file main.go. In the main function:

package main

import (
_ ""

func main() {
user, _ := user.Current()
cwtchbot := bot.NewCwtchBot(path.Join(user.HomeDir, "/.echobot/"), "echobot")

// Set Some Profile Information
cwtchbot.Peer.SetScopedZonedAttribute(attr.PublicScope, attr.ProfileZone, constants.Name, "echobot2")
cwtchbot.Peer.SetScopedZonedAttribute(attr.PublicScope, attr.ProfileZone, constants.ProfileAttribute1, "A Cwtchbot Echobot")

fmt.Printf("echobot address: %v\n", cwtchbot.Peer.GetOnion())

for {
message := cwtchbot.Queue.Next()
cid, _ := cwtchbot.Peer.FetchConversationInfo(message.Data[event.RemotePeer])
switch message.EventType {
case event.NewMessageFromPeer:
msg := cwtchbot.UnpackMessage(message.Data[event.Data])
fmt.Printf("Message: %v\n", msg)
reply := string(cwtchbot.PackMessage(msg.Overlay, msg.Data))
cwtchbot.Peer.SendMessage(cid.ID, reply)
case event.ContactCreated:
fmt.Printf("Auto approving stranger %v %v\n", cid, message.Data[event.RemotePeer])
// accept the stranger as a new contact
// Send Hello...
reply := string(cwtchbot.PackMessage(model.OverlayChat, "Hello!"))
cwtchbot.Peer.SendMessage(cid.ID, reply)

Using Imp (Rust)

Imp (Rust) Bot Framework

This tutorial uses the Imp Cwtch Bot framework (Rust). This framework is currently a work-in-progress and the API design is subject to change. IMP is also based on libcwtch-rs which is currently based on an older pre-stable API version of Cwtch. We are planning in updating libcwtch-rs in Summer 2023.

use std::borrow::BorrowMut;
use std::thread;
use chrono::{DateTime, FixedOffset};
use libcwtch;
use libcwtch::CwtchLib;
use libcwtch::structs::*;
use libcwtch::event::*;
use cwtch_imp::imp;
use cwtch_imp::behaviour::*;
use cwtch_imp::imp::Imp;

const BOT_HOME: &str = "~/.cwtch/bots/echobot";
const BOT_NAME: &str = "echobot";

struct Echobot {}

fn main() {
let behaviour: Behaviour = BehaviourBuilder::new().name(BOT_NAME.to_string()).new_contact_policy(NewContactPolicy::Accept).build();
let event_loop_handle = thread::spawn(move || {
let mut echobot = Echobot {};
let mut bot = Imp::spawn(behaviour,String::new(), BOT_HOME.to_string());
event_loop_handle.join().expect("Error running event loop");

impl imp::EventHandler for Echobot {
fn on_new_message_from_contact(&self, cwtch: &dyn libcwtch::CwtchLib, profile: &Profile, conversation_id: ConversationID, handle: String, timestamp_received: DateTime<FixedOffset>, message: Message) {
let response = Message {
o: MessageType::TextMessage,
d: message.d,
cwtch.send_message(&profile.profile_id, conversation_id, &response);

fn handle(&mut self, cwtch: &dyn CwtchLib, profile_opt: Option<&Profile>, event: &Event) {
match event {
Event::NewPeer { profile_id, tag, created, name, default_picture, picture, online, profile_data } => {
"\n***** {} at {} *****\n",
name, profile_id.as_str()
_ => (),