-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Added reverse engineering docs * Update .gitignore * Added protobuf for Hypurr * Added better documentation * Added hyperr utils package * Added env utils * Added function to convert unix ms to time.Time * Added method to extend HyperliquidLaunch * Added functions to sort launches * Exported GetValFromEnv * Added method to format supply * Added session handlers * Added default config * Added helpers to create new launch embeds * Updated webhook color * Added new launch monitor * Added dockerfile * Added docs on how to run docker image
- Loading branch information
1 parent
1e61a20
commit 2915a3e
Showing
26 changed files
with
10,368 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
reverse_engineering/ | ||
.env | ||
.gitignore | ||
README.md |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -23,3 +23,6 @@ go.work.sum | |
|
||
# env file | ||
.env | ||
|
||
# Reverse engineering specifics | ||
reverse_engineering |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
############################################################################### | ||
# Build stage | ||
############################################################################### | ||
FROM golang:latest AS builder | ||
|
||
# Create and/or change our directory to /build | ||
WORKDIR /build | ||
|
||
# Copy go mod & go.sum files so we can verify they haven't been tampered with | ||
COPY go.mod go.sum ./ | ||
RUN go mod download && go mod verify | ||
|
||
# Copy everything from our root into /build | ||
COPY . . | ||
|
||
# Create and/or change our directory to /new-project-monitor | ||
WORKDIR /build/cmd/new-project-monitor | ||
|
||
# Create the binary for the app | ||
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o app-binary | ||
|
||
############################################################################### | ||
# Production stage | ||
############################################################################### | ||
# Start a new stage from scratch so that our final image is way smaller | ||
# We use alpine instead of stratch since we need OS packages | ||
FROM alpine:latest | ||
|
||
# Create and/or change our directory to /app | ||
WORKDIR /app | ||
|
||
# Copy the binary from the build stage to the final stage | ||
COPY --from=builder /build/cmd/new-project-monitor . | ||
|
||
|
||
# Expose Port | ||
EXPOSE 8080 | ||
|
||
# Run the app | ||
CMD ["/app/app-binary"] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,98 @@ | ||
# HypurrFun | ||
Program to monitor HypurrFun releases | ||
|
||
A tool for interacting with hypurr.fun | ||
|
||
## Overview | ||
|
||
This project allows you to monitor and interact releases on HypurrFun by reverse engineering and decoding the binary gRPC data used in their client-server communication. Since the platform doesn't provide official documentation for their `.protobuf` files or enable gRPC reflection, I've made a quick package to help decode their protocol. | ||
|
||
## Features | ||
|
||
- Hypurr Protobuf file | ||
- Streaming support | ||
- New project monitor | ||
- Lightning quick | ||
- Discord webhook notification | ||
|
||
## How to use | ||
|
||
### Docker | ||
|
||
To use the new project monitor, you can simply use Docker to build and run the image. To do so, use: | ||
|
||
Build the image: | ||
|
||
```bash | ||
docker build -t hypurr-project-monitor:latest . | ||
``` | ||
|
||
Run the image: | ||
|
||
```bash | ||
docker run -d -i \ | ||
-e NEW_LAUNCHES_WEBHOOK=<your-webhook-url> \ | ||
--name hypurr-new-releases hypurr-project-monitor:latest | ||
``` | ||
|
||
Replace `<your-webhook-url>` with your actual webhook URL for notifications. | ||
|
||
### Manually | ||
|
||
Alternatively, you can do: | ||
|
||
```bash | ||
go run cmd/new-project-monitor/main.go | ||
``` | ||
|
||
### Integration | ||
|
||
#### Go Integration | ||
|
||
To use this library in your own Go program, simply follow these steps: | ||
|
||
1. Install the package: | ||
|
||
```bash | ||
go get -u github.com/Matthew17-21/HypurrFun | ||
``` | ||
|
||
2. Use in your code: | ||
|
||
```go | ||
package main | ||
|
||
import ( | ||
"log" | ||
hypurrutils "github.com/Matthew17-21/HypurrFun/hypurr_utils" | ||
) | ||
|
||
func main() { | ||
userAgent := "<user-agent>" // Replace with your user agent | ||
|
||
// Initialize the client | ||
client, err := hypurrutils.NewStaticClient(userAgent) | ||
if err != nil { | ||
log.Fatalln(err) | ||
} | ||
|
||
// Your implementation here | ||
} | ||
``` | ||
|
||
#### Other Languages | ||
|
||
The project supports integration with multiple programming languages through Protocol Buffers: | ||
|
||
1. Locate the Proto file at [`pb/hypurr.proto`](/pb/hypurr.proto) | ||
2. Generate code for your target language using `protoc`: | ||
|
||
```bash | ||
protoc --<language>_out=. pb/hypurr.proto | ||
``` | ||
|
||
Replace `<language>` with your desired language (e.g., python, java, cpp). | ||
|
||
## TODOs | ||
|
||
- [ ] Tests | ||
- [ ] Tests for the `hypurr_utils` package |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,131 @@ | ||
package main | ||
|
||
import ( | ||
"context" | ||
"io" | ||
"log" | ||
"time" | ||
|
||
hypurrutils "github.com/Matthew17-21/HypurrFun/hypurr_utils" | ||
launchutils "github.com/Matthew17-21/HypurrFun/internal/launch_utils" | ||
"github.com/Matthew17-21/HypurrFun/internal/webhook" | ||
"github.com/Matthew17-21/HypurrFun/pb" | ||
"github.com/joho/godotenv" | ||
"google.golang.org/grpc" | ||
) | ||
|
||
const userAgent = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36" | ||
|
||
const ( | ||
errorDelay = 1500 * time.Millisecond | ||
) | ||
|
||
func main() { | ||
// Initialize app | ||
initialize() | ||
|
||
// Create a new client | ||
client, err := hypurrutils.NewStaticClient(userAgent) | ||
if err != nil { | ||
log.Fatalln(err) | ||
} | ||
|
||
// Create new context | ||
ctx, cancel := context.WithCancel(context.Background()) | ||
defer cancel() | ||
|
||
// Monitor for new projects | ||
monitorNewLaunches(ctx, client) | ||
} | ||
|
||
// initialize sets up the application environment, such as loading `.env` variables. | ||
func initialize() { | ||
log.Println("Initializing application...") | ||
|
||
// Attempt to load environment variables from a `.env` file. | ||
// If the file is missing or cannot be read, log a warning and proceed. | ||
if err := godotenv.Load(); err != nil { | ||
log.Printf("WARNING: Failed to load environment variables from .env file: %v", err) | ||
} | ||
} | ||
|
||
func monitorNewLaunches(ctx context.Context, client pb.StaticClient) { | ||
log.Println("Monitoring for new Hypurr.fun launches...") | ||
|
||
// Create a function to establish the stream | ||
establishStream := func() (grpc.ServerStreamingClient[pb.HyperliquidLaunchStreamResponse], error) { | ||
return client.HyperliquidLaunchStream(ctx, &pb.HyperliquidLaunchStreamRequest{}) | ||
} | ||
|
||
// Create the stream | ||
launchesStream, err := establishStream() | ||
if err != nil { | ||
log.Fatalln("error with stream:", err) | ||
} | ||
|
||
// Define a var to reference when getting new coins | ||
// No need to have an array of launches, we only need to keep track of the lastest one | ||
var latestCoin *launchutils.LaunchExtended | ||
|
||
// Listen to for new events | ||
for { | ||
resp, err := launchesStream.Recv() | ||
if err != nil { | ||
// Check to see if stream is closed | ||
if err == io.EOF { | ||
log.Println("Stream is closed.") | ||
return | ||
} | ||
log.Println("Error receiving message from stream:", err) | ||
launchesStream.CloseSend() | ||
time.Sleep(errorDelay) | ||
|
||
// Attempt to reestablish the stream | ||
launchesStream, err = establishStream() | ||
if err != nil { | ||
log.Fatalln("error with stream:", err) | ||
} | ||
continue | ||
} | ||
|
||
// The first response message from the stream will be a list of tokens sorted by latest activity | ||
// Update our initial array to to that | ||
if latestCoin == nil { | ||
// Sort by listed timestamp in descending order (most recent first) | ||
tmp := launchutils.SortByListedTimestampDesc(resp.Launches) | ||
if len(tmp) == 0 { | ||
continue | ||
} | ||
log.Printf("Received initial launch list with %d launches\n", len(tmp)) | ||
|
||
// Get the first coin (latest one) and set it as the latest | ||
latestCoin = launchutils.ToLaunchExtended(tmp[0]) | ||
continue | ||
} | ||
|
||
// For subsequent messages, if the listed timestamp is newer than the first element, add to array & send webhook | ||
for _, launch := range resp.Launches { | ||
// Filter out launches that are either: | ||
// - Duplicates (same ID as an existing launch) | ||
// - Or out of chronological order (earlier timestamp than what we already have) | ||
coin := launchutils.ToLaunchExtended(launch) | ||
if shouldSkipLaunch(coin, latestCoin) { | ||
continue | ||
} | ||
log.Printf("New launch detected: %+v\n", launch) | ||
|
||
// Send webhook | ||
webhook.SendNewLaunch(coin) | ||
|
||
// Update the latest coin | ||
latestCoin = coin | ||
} | ||
} | ||
} | ||
|
||
func shouldSkipLaunch(coin, latestCoin *launchutils.LaunchExtended) bool { | ||
return coin == nil || | ||
latestCoin == nil || | ||
coin.Id == latestCoin.Id || | ||
coin.LaunchTime.Before(latestCoin.LaunchTime) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
module github.com/Matthew17-21/HypurrFun | ||
|
||
go 1.22.2 | ||
|
||
require ( | ||
github.com/Monumental-Shopping/go-discord-webhook v1.1.1 | ||
github.com/joho/godotenv v1.5.1 | ||
github.com/stretchr/testify v1.8.4 | ||
github.com/test-go/testify v1.1.4 | ||
google.golang.org/grpc v1.69.2 | ||
google.golang.org/protobuf v1.36.1 | ||
) | ||
|
||
require ( | ||
github.com/davecgh/go-spew v1.1.1 // indirect | ||
github.com/pmezard/go-difflib v1.0.0 // indirect | ||
golang.org/x/net v0.33.0 // indirect | ||
golang.org/x/sys v0.28.0 // indirect | ||
golang.org/x/text v0.21.0 // indirect | ||
google.golang.org/genproto/googleapis/rpc v0.0.0-20241230172942-26aa7a208def // indirect | ||
gopkg.in/yaml.v3 v3.0.1 // indirect | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
github.com/Monumental-Shopping/go-discord-webhook v1.1.1 h1:s4Xit/WweCncUuapna0H2xmYA0/ZALX6il3UJNz2GTI= | ||
github.com/Monumental-Shopping/go-discord-webhook v1.1.1/go.mod h1:WV+RisyjGyR9IzjLMzcSABNzIvFbYTk354DWQN54sxI= | ||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= | ||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= | ||
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= | ||
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= | ||
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= | ||
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= | ||
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= | ||
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= | ||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= | ||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= | ||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= | ||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= | ||
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= | ||
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= | ||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= | ||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= | ||
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= | ||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= | ||
github.com/test-go/testify v1.1.4 h1:Tf9lntrKUMHiXQ07qBScBTSA0dhYQlu83hswqelv1iE= | ||
github.com/test-go/testify v1.1.4/go.mod h1:rH7cfJo/47vWGdi4GPj16x3/t1xGOj2YxzmNQzk2ghU= | ||
go.opentelemetry.io/otel v1.31.0 h1:NsJcKPIW0D0H3NgzPDHmo0WW6SptzPdqg/L1zsIm2hY= | ||
go.opentelemetry.io/otel v1.31.0/go.mod h1:O0C14Yl9FgkjqcCZAsE053C13OaddMYr/hz6clDkEJE= | ||
go.opentelemetry.io/otel/metric v1.31.0 h1:FSErL0ATQAmYHUIzSezZibnyVlft1ybhy4ozRPcF2fE= | ||
go.opentelemetry.io/otel/metric v1.31.0/go.mod h1:C3dEloVbLuYoX41KpmAhOqNriGbA+qqH6PQ5E5mUfnY= | ||
go.opentelemetry.io/otel/sdk v1.31.0 h1:xLY3abVHYZ5HSfOg3l2E5LUj2Cwva5Y7yGxnSW9H5Gk= | ||
go.opentelemetry.io/otel/sdk v1.31.0/go.mod h1:TfRbMdhvxIIr/B2N2LQW2S5v9m3gOQ/08KsbbO5BPT0= | ||
go.opentelemetry.io/otel/sdk/metric v1.31.0 h1:i9hxxLJF/9kkvfHppyLL55aW7iIJz4JjxTeYusH7zMc= | ||
go.opentelemetry.io/otel/sdk/metric v1.31.0/go.mod h1:CRInTMVvNhUKgSAMbKyTMxqOBC0zgyxzW55lZzX43Y8= | ||
go.opentelemetry.io/otel/trace v1.31.0 h1:ffjsj1aRouKewfr85U2aGagJ46+MvodynlQ1HYdmJys= | ||
go.opentelemetry.io/otel/trace v1.31.0/go.mod h1:TXZkRk7SM2ZQLtR6eoAWQFIHPvzQ06FJAsO1tJg480A= | ||
golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I= | ||
golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4= | ||
golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= | ||
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= | ||
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= | ||
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= | ||
google.golang.org/genproto/googleapis/rpc v0.0.0-20241230172942-26aa7a208def h1:4P81qv5JXI/sDNae2ClVx88cgDDA6DPilADkG9tYKz8= | ||
google.golang.org/genproto/googleapis/rpc v0.0.0-20241230172942-26aa7a208def/go.mod h1:bdAgzvd4kFrpykc5/AC2eLUiegK9T/qxZHD4hXYf/ho= | ||
google.golang.org/grpc v1.69.2 h1:U3S9QEtbXC0bYNvRtcoklF3xGtLViumSYxWykJS+7AU= | ||
google.golang.org/grpc v1.69.2/go.mod h1:vyjdE6jLBI76dgpDojsFGNaHlxdjXN9ghpnd2o7JGZ4= | ||
google.golang.org/protobuf v1.36.1 h1:yBPeRvTftaleIgM3PZ/WBIZ7XM/eEYAaEyCwvyjq/gk= | ||
google.golang.org/protobuf v1.36.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= | ||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= | ||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= | ||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= | ||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
package hypurrutils | ||
|
||
import ( | ||
"fmt" | ||
|
||
"github.com/Matthew17-21/HypurrFun/pb" | ||
) | ||
|
||
// NewStaticClient creates a new gRPC client to communicate with Hypurr.fun's static servers | ||
func NewStaticClient(userAgent string) (pb.StaticClient, error) { | ||
conn, err := createGRPCConnection(userAgent) | ||
if err != nil { | ||
return nil, fmt.Errorf("createGRPCConnection error: %w", err) | ||
} | ||
return pb.NewStaticClient(conn), nil | ||
} | ||
|
||
// NewTelegramClient creates a new gRPC client to communicate with Hypurr.fun's telegram servers | ||
func NewTelegramClient(userAgent string) (pb.TelegramClient, error) { | ||
conn, err := createGRPCConnection(userAgent) | ||
if err != nil { | ||
return nil, fmt.Errorf("createGRPCConnection error: %w", err) | ||
} | ||
return pb.NewTelegramClient(conn), nil | ||
} |
Oops, something went wrong.