├── .gitignore ├── LICENSE ├── README.md ├── bun.lockb ├── index.ts └── package.json /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Dawson Botsford 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Opencast 2 | 3 | Opencast is a free and open-source indexer designed to empower developers by providing a flexible and powerful way to query data from Farcaster hubs and nodes. 4 | 5 | ## Overview 6 | 7 | Opencast offers an alternative to closed platforms like Naynar, enabling developers to create sophisticated queries that unlock valuable insights from their Farcaster data. 8 | 9 | ## Problem 10 | 11 | - **Limited Capabilities:** Existing platforms restrict the types of queries you can execute. Opencast breaks these barriers, allowing you to build more dynamic Frames. 12 | - Example Queries: 13 | - Identify optimal times for casting content 14 | - Analyze the performance of your casts sv 15 | 16 | - **Data Dependence:** Reliance on third-party platforms creates vulnerabilities like uptime issues and potential paywalls. Opencast empowers you to own and control your data, eliminating such dependencies. 17 | 18 | ## Solution 19 | 20 | Opencast delivers a unique combination of features: 21 | 22 | - **Client-Side Agnosticism:** Seamlessly integrate Opencast with your existing client-side applications, regardless of their architecture. 23 | - **Node and Protocol Querying:** Execute powerful queries directly against Farcaster nodes and the underlying protocol, unlocking a wider range of data. 24 | - **Expansive Querying (Future):** A future release will introduce even richer querying capabilities, empowering you to delve deeper into your data. 25 | 26 | ## Target Audience 27 | 28 | - **Short-Term:** 29 | - Developers who operate their own Farcaster hubs (nodes) 30 | - Client-side developers seeking to integrate Farcaster data querying 31 | - **Long-Term:** 32 | - Builders envisioning applications that don't require self-hosted hubs 33 | 34 | ## Minimum Viable Product (MVP) 35 | 36 | The MVP will allow users to interact with the Opencast backend, crafting custom queries to extract valuable data from Farcaster. A demo showcasing both backend functionality and client-side usage examples will be provided. 37 | 38 | ## Architecture 39 | 40 | Opencast is built on a robust technology stack: 41 | 42 | - **Backend:** 43 | - GraphQL: Enables efficient and flexible data querying. 44 | - Shuttle: Provides real-time communication between the backend and client-side components. 45 | - Elysia: (Optional) Streamlines data fetching and manipulation. 46 | - Bun: (Optional) A high-performance JavaScript runtime for a fast and efficient backend. 47 | 48 | - **Client-Side:** 49 | - Opencast is agnostic to the chosen client-side application. It can be used locally as a developer tool, seamlessly integrating with various frameworks and libraries. 50 | 51 | ## User Interface (UI) 52 | 53 | Since Opencast is primarily a developer tool, it doesn't have a traditional user interface. Instead, it exposes a series of well-defined, programmatic endpoints that developers can leverage to build client-side applications with querying capabilities. 54 | 55 | ## Tooling 56 | 57 | - **Error Monitoring:** Sentry (https://sentry.io/resources/error-monitoring/) provides real-time insights into errors and facilitates their timely resolution. 58 | - **Real-time Communication:** Shuttle (https://docs.farcaster.xyz/developers/resources) enables communication between the Opencast backend and client-side application. 59 | - **Farcaster Client/SDK Alternatives:** 60 | - Litecast (https://github.com/dylsteck/litecast) 61 | - Search for relevant Farcaster client libraries on GitHub by visiting https://github.com/farcasterxyz 62 | 63 | ## Getting Started 64 | 65 | (Instructions on installation, usage, and contributing will be added in a separate document) 66 | 67 | ## Contributing 68 | 69 | We welcome contributions from the community! (Information on contribution guidelines will be added in a separate document) 70 | 71 | ## License 72 | 73 | Opencast is licensed under the [MIT License](https://choosealicense.com/licenses/mit/). 74 | 75 | ## Disclaimer 76 | 77 | This Readme.md is a work in progress. We'll continue to update it with more detailed information as development progresses. Ping [@DawsonBotsford 78 | ](https://twitter.com/DawsonBotsford) or [@sydneylai](https://twitter.com/sydneylai) for collaboration. 79 | -------------------------------------------------------------------------------- /bun.lockb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dawsbot/opencast/c7a1789cf0a580bb857f52b5f178014b9b6f28ec/bun.lockb -------------------------------------------------------------------------------- /index.ts: -------------------------------------------------------------------------------- 1 | import { Elysia, t } from "elysia"; 2 | import postgres from "postgres"; 3 | import { swagger } from "@elysiajs/swagger"; 4 | import yoga from "@elysiajs/graphql-yoga"; 5 | 6 | const sql = postgres( 7 | "postgresql://admin:0rDgEmOqn9pTl2ld0Wg9RpCYOjwrprjd@dpg-cocoh3nsc6pc73d43el0-a.ohio-postgres.render.com/fc_qsro", 8 | { ssl: true } 9 | ); 10 | 11 | const app = new Elysia().use(swagger()); 12 | 13 | async function selectCastsByFid(fid: number) { 14 | const rows = await sql`SELECT * 15 | FROM casts 16 | WHERE fid = ${fid} 17 | AND deleted_at IS NULL;`; 18 | 19 | return rows.map((row) => ({ 20 | ...row, 21 | hash: row.hash.toString("hex"), 22 | })); 23 | } 24 | 25 | async function selectReactionsByTargetCastHash(targetCastHash: string) { 26 | console.log({ targetCastHash }); 27 | const rows = await sql`SELECT * 28 | FROM reactions 29 | WHERE target_cast_hash = decode(${targetCastHash}, 'hex') 30 | AND deleted_at IS NULL;`; 31 | 32 | console.dir({ rows }); 33 | return rows; 34 | } 35 | 36 | app 37 | .use( 38 | yoga({ 39 | typeDefs: /* GraphQL */ ` 40 | type Query { 41 | casts(userFid: Int!): [Cast!] 42 | reactions(targetCastHash: String!): [Reaction!] 43 | hi: String 44 | } 45 | 46 | type Cast { 47 | id: ID! 48 | created_at: String! 49 | updated_at: String! 50 | timestamp: String! 51 | deleted_at: String 52 | fid: String! 53 | parent_fid: String 54 | hash: String! 55 | root_parent_hash: String 56 | parent_hash: String 57 | root_parent_url: String 58 | parent_url: String 59 | text: String! 60 | mentions: [String!] 61 | mentions_positions: [Int!] 62 | } 63 | type Reaction { 64 | id: ID! 65 | created_at: String! 66 | updated_at: String! 67 | timestamp: String! 68 | deleted_at: String 69 | fid: String! 70 | target_cast_fid: String! 71 | type: Int! 72 | hash: String! 73 | target_url: String 74 | } 75 | `, 76 | resolvers: { 77 | Query: { 78 | casts: async (_parent, { userFid }) => 79 | await selectCastsByFid(userFid), 80 | reactions: async (_parent, { targetCastHash }) => 81 | await selectReactionsByTargetCastHash(targetCastHash), 82 | hi: () => "Hello from Elysia", 83 | }, 84 | }, 85 | }) 86 | ) 87 | .get( 88 | "/casts/:fid", 89 | async ({ params: { fid } }) => { 90 | const casts = await selectCastsByFid(fid); 91 | return { casts }; 92 | }, 93 | { 94 | params: t.Object({ 95 | fid: t.Numeric(), 96 | }), 97 | } 98 | ); 99 | 100 | app.listen(8080, () => 101 | console.log("Elysia server running on http://localhost:8080") 102 | ); 103 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { "dependencies": { "@elysiajs/graphql-yoga": "^1.0.3", "@elysiajs/swagger": "^1.0.5", "elysia": "^1.0.16", "postgres": "^3.4.4" } } --------------------------------------------------------------------------------