Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ TWITTER_API_KEY=<Your Twitter API Key>
TWITTER_API_SECRET_KEY=<Your Twitter API Secret Key>
TWITTER_ACCESS_TOKEN=<Your Twitter Access Token>
TWITTER_ACCESS_TOKEN_SECRET=<Your Twitter Access Token Secret>
NUGGETS_CLIENT_ID=<Your Nuggets OIDC client ID>
NUGGETS_PRIVATE_KEY=<Your Nuggets OIDC client private key>

# GATED DATA (optional). Server will start without these and not use the feature
ORBIS_CONTEXT_ID=<Orbis server context ID>
Expand Down
24 changes: 24 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,30 @@ pnpm run dev
GITHUB_CLIENT_SECRET=1234567890abcdef1234567890abcdef12345678
```

- `NUGGETS_CLIENT_ID` & `NUGGETS_PRIVATE_KEY`: Authentication credentials for Nuggets OIDC integration

> *Note*: OIDC clients can be created for both personal, and organsiation accounts. Verified information, such as business information and verified social accoounts (i.e. X) is added to the user interaction screens.

1. Go to [Nuggets Account Portal](https://accounts.nuggets.life)
2. Sign in to the the portal using the Nuggets App (Nuggets PAY & ID) from your relevant app store
3. Once signed in, you can create a Client application under the "Checks" menu option
4. Select the "Advanced" tab for OIDC clients & click "Create"
5. Under "Advanced Checks" settings:
- Name: Your app name
- Redirect URLs: `http://{NGROK_DOMAIN}/auth` (development)
- Sign out callback URLs: `http://{NGROK_DOMAIN}/auth/github/callback`
6. After creating the app:
- Copy "Client ID" β†’ `NUGGETS_CLIENT_ID`
- JSON Stringify downlaoded private key β†’ `NUGGETS_PRIVATE_KEY`
7. Example:
```env
GITHUB_CLIENT_ID=1234567890abcdef1234
GITHUB_CLIENT_SECRET={"kty":"RSA","kid":"vTNjuNZqsUDRflSgSTXC8VXl91l62W1009ZZivPv2_U","use":"sig","alg":"RS256","e":"AQAB","n":"hsuAaljoIonGYr091eDLWhoPK7wTX1JzSIjoRVKLfhwwiOeNWcY2akRks1CFssYUAHHN9wOEwYzYG169uWHK6PGiIxV6UBBeX6-0KIIEtMiOqgTj5PQpQ9I-Lja0dJUXAaIj5o9KnPIz5-4yqaey1_InNAjJ-MquiA3uqXyKrfN7-b5qRYU4cC4FTvsA9bMAefS2poJ3vIM4vEB8ReViF5NBm-FwcVwu4RDlKSeGaBKp4wQjDOedtqS-8aMKkDAHMHjfePw_HARj83TnqLDLfrHdU8ZFnhYevn0Hrcphfvszip5_JrDsTbz2AiEQsEbbNIKM_POp67iVIJB6Cug-5Q","d":"H2pKUX7LC1Gk5NpKERDFFAjCxGSe9zZWG--cgpuMWyXiweWFcgjaIcYy2f_ISTQU8U2UvR8az6ktrKNWAGCkU8oNCbQ1v3cxJ4fhqxcyVZfwd_uPyYqGG4Jib47cW9drdzv8tf1rXEyugD1pnVSyP9X2ARCTUeKa7qagmuulvoiMIUzVd0dKWkuYS5mdvQjlmAazTvi5ubRBkhKSJLc6fiHafjNqp4vTtJ0v_wMPz2slxvkCayK1xVOPO2cdi8QvI3EFx8JXfGgRV6DtW_4W-uUWuLltDt4aF57eveUsn0yxvvfuzfYfyYKtJIZidaN21zwsGXaOz5K43KSps44uwQ","p":"vIXzyNcy_FUWzHoRv6OdzpVUCejuTTK_Vw-JHzsBH0IwBu5Ytss4sQP0z6vbHW3feG3XU_c7xghkq3GBhGyIL36mewgSzcwlNmuvaa9fPGKs-j2WggiDXZ-jT5h-J-mNSAsi2BPhW3l21wKYtmLNZvqJB-4fG9EhVJrUBpEnkok","q":"twqE9gk-INw0xxMpQgN_ttJruQPFbi0lm4J6N622uCJhLIf1YtA7o6sDq6BxvRtnOzXXNNoeeByURYgAqFDhSExIs3hzur53ENdpseqC8-QBGwlP4XrnlHmkjHWjeLIig3zoQ7gNyPMSa7UoTGyvC8v5qj0lM7qgw8SP_xZDon0","dp":"P0VJoy8uSmeYRjn1uPaFoO6XDNhjGf-dHe_12khoQQBqs_jMR1T_CtHEGOqZmE_mvzlGGkUQh48Lkkp-6F2wexgrcYEtJmqMVq5fab3U3aRUX4vJ9ow64nIQUkdgl66XnwU1xYUujg--5bxWWmfgfd45nTK6uBBA-m8YF7ghqtk","dq":"MzHUAmSPZe1xLYTkczw7Sk0MKBEv9SLXc-lIea4yfsnO1_-m2F5Zb-ty-VvEtBmoA92JNZUUFJAZwGDLnkgQ2oHLi7bS15ZSAvnc7BAlVEWfMYqnNu-DOfnT1h1I_YcFNDWJWPdvk5a4vjtK7CUxyGDikYTMlO1L7bVaeQQnIpk","qi":"KvCjnUt8KPrwOb469i4YXqlr8fnSwDS98-hDYYZUKE47cbDVqCuziPM4gP5gAQFoyDo-vhBKCurFPIyD8QiPTFGkUMD0YeE71HymO1uCgLzmFrrT06KY3O1jes9rp5hy43PuGJ13BiAYPCLXPUyzxCMNadjek9kaQLItqxyXTC4"}
```
8. Set the `ELIZA_CHARACTER_PATH` -> `ELIZA_CHARACTER_PATH=nuggets_character.json`
9. Test on the client `/nuggets` page by saying hello to Eliza to start the verification flow


- `ORBIS_CONTEXT_ID`, `ORBIS_TABLE_ID`, & `ORBIS_ENV`: OrbisDB table identifiers to enable gated memory storage functionality

1. Visit the [Orbis Studio](https://studio.useorbis.com/) and log in with your browser wallet. Once logged in, set up a new context under the `Contexts` tab. Assign that value to `ORBIS_CONTEXT_ID` in your .env file
Expand Down
2 changes: 2 additions & 0 deletions client/app/(routes)/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import TelegramUser from "../_components/TelegramUser";
import { TwitterLogin } from "../_components/TwitterLogin";
import { DiscordLogin } from "../_components/DiscordLogin";
import { GithubLogin } from "../_components/GithubLogin";
import { NuggetsDemoPageBtn } from "../_components/NuggetsDemoPageBtn";

export default function Home(): ReactElement {
return (
Expand All @@ -16,6 +17,7 @@ export default function Home(): ReactElement {
<TwitterLogin />
<DiscordLogin />
<GithubLogin />
<NuggetsDemoPageBtn />
</div>
</main>
);
Expand Down
112 changes: 112 additions & 0 deletions client/app/_components/NuggetsChat.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
"use client";

import { useEffect, useState } from "react";
import axios from "axios";

interface Message {
text: string;
isUser: boolean;
}

export default function HelloWorld() {
const [isConfigured, setIsConfigured] = useState<boolean>(
!!process.env.NEXT_PUBLIC_API_URL
);
const [messages, setMessages] = useState<Message[]>([]);
const [input, setInput] = useState<string>("");
const [error, setError] = useState<string>("");

useEffect(() => {
setIsConfigured(!!process.env.NEXT_PUBLIC_API_URL);
}, []);

const sendMessage = async (e: React.FormEvent) => {
e.preventDefault();
if (!input.trim()) return;

const userMessage = input.trim();
setInput("");
setMessages(prev => [...prev, { text: userMessage, isUser: true }]);

try {
const response = await axios.post("/api/chat/eliza", { message: userMessage });
setMessages(prev => [...prev, { text: response.data.response, isUser: false }]);
} catch (err: any) {
setError(err.message);
}
};

return (
<div className="flex flex-col items-center w-full max-w-2xl mx-auto p-4">
<h1 className="text-2xl font-bold mb-4">Chat with Eliza</h1>

{!isConfigured ? (
<span>
API URL not configured properly, type Ctrl+C and then type{" "}
<span className="text-blue-500">
<b>pnpm run dev</b>
</span>{" "}
in the terminal
</span>
) : (
<>
<div className="w-full h-[400px] border rounded-lg p-4 mb-4 overflow-y-auto">
{messages.map((message, index) => {
let messageText = <div>{message.text}</div>

// check if message contains nuggets deeplink
if (message.text.match(/ class="nuggets-deeplink"/)) {
messageText = <div dangerouslySetInnerHTML={{ __html: message.text }}></div>
}

return (
<div
key={index}
className={`mb-2 ${
message.isUser ? "text-right" : "text-left"
}`}
>
<span
className={`inline-block p-2 rounded-lg ${
message.isUser
? "bg-blue-500 text-white"
: "bg-gray-200 text-black"
}`}
>
{messageText}
</span>
</div>
)
})}
</div>

<form onSubmit={sendMessage} className="w-full flex gap-2">
<input
type="text"
value={input}
onChange={(e) => setInput(e.target.value)}
className="flex-1 p-2 border rounded text-black"
placeholder="Type your message..."
/>
<button
type="submit"
className="px-4 py-2 bg-blue-500 text-white rounded bg-[#8665a1] hover:bg-[#8665a1]"
>
Send
</button>
</form>

{error && (
<div className="text-red-500 mt-2">Error: {error}</div>
)}

<div style={{ width: '175px' }}>
<a href="https://nuggets.life">
<img src="https://nuggets-public-assets.s3.eu-west-1.amazonaws.com/Nuggets+Logotype+and+Icon+White+Outline+Grey+Outline+Horizontal+Powered+By.png" alt="Powered by Nuggets" />
</a>
</div>
</>
)}
</div>
);
}
23 changes: 23 additions & 0 deletions client/app/_components/NuggetsDemoPageBtn.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
"use client";

import { Button } from "@/components/ui/button";

export function NuggetsDemoPageBtn() {

const handleNuggetsDemoLinkClick = async (e: React.MouseEvent) => {
e.preventDefault();
window.location.href = '/nuggets';
};

return (
<div className="flex items-center justify-center w-full my-4">
<Button
type="button"
onClick={handleNuggetsDemoLinkClick}
className="flex items-center gap-2 bg-[#8665a1] hover:bg-[#8665a1] text-white rounded"
>
<span>Nuggets Eliza Demo</span>
</Button>
</div>
);
}
29 changes: 29 additions & 0 deletions client/app/api/chat/eliza/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { NextResponse } from "next/server";

export async function POST(request: Request) {
try {
const body = await request.json();
const { message } = body;

// Make the call to your backend Eliza service
const response = await fetch(`http://localhost:3001/eliza`, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ message }),
});

if (!response.ok) {
throw new Error("Failed to get response from Eliza");
}

const data = await response.json();
return NextResponse.json(data);
} catch (error: any) {
return NextResponse.json(
{ error: error.message || "Failed to process request" },
{ status: 500 }
);
}
}
14 changes: 14 additions & 0 deletions client/app/nuggets/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
"use client";

import { ReactElement } from "react";
import NuggetsChat from "../_components/NuggetsChat";

export default function Nuggets(): ReactElement {
return (
<main className="min-h-screen w-full flex items-center justify-center p-4">
<div className="w-full max-w-4xl mx-auto">
<NuggetsChat />
</div>
</main>
);
}
10 changes: 9 additions & 1 deletion client/styles/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,12 @@ body {
focus:outline-none focus:ring-1 focus:ring-primary
rounded-lg;
}
}

.nuggets-deeplink {
color: #8665a1;
}

.nuggets-deeplink:hover {
text-decoration: underline;
}
}
83 changes: 83 additions & 0 deletions nuggets_character.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
{
"name": "Barry",
"clients": [],
"modelProvider": "openai",
"settings": {
"secrets": {},
"voice": {
"model": "en_UK-male-medium"
}
},
"people": [
"Alastair Johnson",
"Seema Khinda Johnson",
"Drummond Reed",
"Andrew Lord"
],
"plugins": [],
"bio": [
"I'm a general personal assistant, helping a user with their requests"
],
"lore": [
"I was created to help users carry out generalised tasks"
],
"knowledge": [
"expert in self-sovereign identity technologies (DIDs, DIDComm & VCs)",
"understands Decentralised Identifier operations and management",
"knows how to interact with Decentralised Identifiers over DIDComm",
"familiar with secure and private payments within confidential compute",
"proficient verifiable credentials and verifiable credential verification",
"experienced with Zero-Knowledge Proofs",
"knowledgeable about blockchain wallet signing and verification",
"understands security and privacy best practices",
"uses cryptographic methods to preserve data privacy and maintain security"
],
"messageExamples": [],
"postExamples": [
"Verifying blockchain account ownership",
"Verifying KYC compliance",
"Providing KYB Proof of business identity",
"Streamlining trust management",
"Making trust accessible"
],
"topics": [
"self-sovereign identity",
"security",
"privacy",
"non-correlation"
],
"style": {
"all": [
"uses precise self-sovereign terminology",
"maintains professional tone",
"focuses on security and privacy",
"explains technical concepts clearly",
"emphasizes accuracy in verification operations"
],
"chat": [
"responds promptly to verification queries",
"guides users through account verification by generating an invite link"
],
"post": [
"highlights self-sovereign identity capabilities",
"emphasizes secure and private operations",
"focuses on verification functionality",
"maintains professional demeanour"
]
},
"adjectives": [
"private",
"secure",
"knowledgeable",
"efficient",
"reliable",
"trustworthy",
"professional",
"technical",
"helpful",
"accurate",
"thorough",
"responsive",
"capable"
]
}
4 changes: 4 additions & 0 deletions server/bin/validate-env
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,10 @@ const ENV_HINTS = {
"Enter your Twitter access token, you can get it from Twitter Developer Portal, go to https://developer.twitter.com/en/portal/dashboard to create one",
TWITTER_ACCESS_TOKEN_SECRET:
"Enter your Twitter access token secret, you can get it from Twitter Developer Portal, go to https://developer.twitter.com/en/portal/dashboard to create one",
NUGGETS_CLIENT_ID:
"Enter your Nuggets OIDC Client ID, you can get it from the Nuggets Accounts Portal, go to https://accounts.nuggets.life to create one",
NUGGETS_PRIVATE_KEY:
"Enter your Nuggets OIDC client private key in stringified JWK format, you can get it from the Nuggets Accounts Portal, go to https://accounts.nuggets.life to create one",
// Add more hints as needed from .env.example
ORBIS_CONTEXT_ID:
"[Press enter/return to skip] (optional - gated data) Visit the Orbis Studio (https://studio.useorbis.com/) and log in with your browser wallet. Once logged in, set up a new context under the `Contexts` tab and use the value here",
Expand Down
3 changes: 2 additions & 1 deletion server/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
"agent-twitter-client": "^0.0.18",
"@useorbis/db-sdk": "0.0.60-alpha",
"axios": "^1.7.7",
"better-sqlite3": "^11.6.0",
"better-sqlite3": "^11.7.0",
"cookie-parser": "^1.4.7",
"cors": "^2.8.5",
"dotenv": "^16.4.5",
Expand All @@ -33,6 +33,7 @@
"grammy": "^1.32.0",
"http-errors": "^2.0.0",
"jsonc-parser": "^3.3.1",
"openid-client": "^6.1.7",
"node-cache": "^5.1.2",
"prettier": "^3.4.2",
"tsc-watch": "^6.2.1"
Expand Down
4 changes: 4 additions & 0 deletions server/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import twitterRouter from "./routes/twitter.js";
import discordRouter from "./routes/discord.js";
import cookieParser from "cookie-parser";
import githubRouter from "./routes/github.js";
import elizaRouter from "./routes/eliza.js";
import { AnyType } from "./utils.js";
import { isHttpError } from "http-errors";

Expand Down Expand Up @@ -56,6 +57,9 @@ app.use("/auth/discord", discordRouter);
// Mount GitHub OAuth routes
app.use("/auth/github", githubRouter);

// Mount Eliza chatbot routes
app.use("/eliza", elizaRouter);

// 404 handler
app.use((_req: Request, _res: Response, _next: NextFunction) => {
_res.status(404).json({
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { AnyType, getCollablandApiUrl } from "../../utils.js";
import { AnyType, getCollablandApiUrl } from "../../../utils.js";
import { Action, ActionExample, Validator, Handler } from "@ai16z/eliza";
import axios, { AxiosInstance } from "axios";

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ import {
} from "@ai16z/eliza";
import { CollabLandBaseAction } from "./collabland.action.js";
import { randomUUID } from "crypto";
import { chainMap } from "../../utils.js";
import { BotAccountMemory, BotAccountResponse } from "../types.js";
import { chainMap } from "../../../utils.js";
import { BotAccountMemory, BotAccountResponse } from "../../types.js";

export class GetBotAccountAction extends CollabLandBaseAction {
constructor() {
Expand Down
Loading