This feature allows users to create and browse lowball offers for Hypixel Skyblock items through both in-game commands and REST API endpoints.
Lowball offers are stored in Cassandra with a 7-day TTL and published to Kafka topic sky-lowball-offers. The system provides:
- Dual indexing (by user and by item tag)
- Pagination using "before DateTime" pattern
- Filter matching similar to flip filters
- In-game command integration for easy offer creation
Two Cassandra tables:
- Partition Key:
user_id - Clustering Keys:
created_at(descending),offer_id - Fields: ItemTag, ItemName, NbtData, Filters, AskingPrice, Lore, ItemCount
- TTL: 7 days
- Partition Key:
item_tag - Clustering Keys:
created_at(descending),offer_id - Fields: UserId, ItemName, NbtData, Filters, AskingPrice, Lore, ItemCount
- TTL: 7 days
Methods:
CreateOffer(userId, item, askingPrice, filters)- Create new offer, store in both tables, publish to KafkaGetOffersByUser(userId, before?, limit)- Paginated user offersGetOffersByItem(itemTag, filters?, before?, limit)- Paginated item offers with filter matchingDeleteOffer(userId, offerId)- Delete offer from both tables
Kafka Integration:
- Topic:
sky-lowball-offers - Producer:
KafkaCreator.BuildProducer<string, LowballOffer>() - Key: offer_id (Guid string)
- Value: LowballOffer object
GET /api/lowball/user/{userId}
- Get offers by user
- Query params:
before(DateTimeOffset, optional) - Pagination cursorlimit(int, default 20, max 100)
- Returns:
List<LowballOffer>
GET /api/lowball/item/{itemTag}
- Get offers by item tag with filter matching
- Query params:
before(DateTimeOffset, optional) - Pagination cursorlimit(int, default 20, max 100)filter(Dictionary<string, string>, optional) - Filter criteria
- Returns:
List<LowballOfferByItem>
DELETE /api/lowball/user/{userId}/offer/{offerId}
- Delete a specific offer
- Returns: 200 OK or 404 Not Found
Usage: /cl offer_lowball|<nbt>|<price>
When a player holds an item and executes the command with their desired asking price:
- NBT data is parsed and item details extracted
- Item filters are automatically requested from the pricing API
- Offer is created and stored
- Confirmation message is shown to the player
Example:
/cl offer_lowball|{...nbt...}|5000000
Response:
Lowball offer created!
Item: Hyperion
Asking price: 500M
Offer ID: 3fa85f64-5717-4562-b3fc-2c963f66afa6
Your offer will be visible for 7 days
The system uses "before DateTime" pattern for pagination:
- First request:
/api/lowball/item/HYPERION?limit=20 - Get the
created_atof the last item in results - Next request:
/api/lowball/item/HYPERION?before=2024-01-15T10:30:00Z&limit=20
This works because created_at is a clustering key with descending order.
Filters work similar to flip filters:
When a user creates an offer via HotkeyCommand, filters are automatically extracted from the item:
- Reforge
- Tier (rarity)
- Enchantments
- Other item-specific attributes
GET /api/lowball/item/HYPERION?filter[reforge]=withered&filter[tier]=LEGENDARY
The service will only return offers where ALL specified filters match the offer's stored filters.
- Dual Indexing: Separate tables for user-centric and item-centric queries without expensive secondary indexes
- TTL: 7-day automatic expiration reduces storage and keeps offers fresh
- Kafka Publishing: Enables downstream processing, analytics, and notifications
- Clustering Key Pattern:
created_at DESC+offer_idensures chronological ordering and uniqueness - Filter Overlap Fetching: Fetch 3x limit then filter to handle sparse filter matches efficiently
Ensure these are configured in appsettings.json:
{
"TOPICS": {
"sky-lowball-offers": "sky-lowball-offers"
},
"KAFKA": {
"BROKERS": "kafka-broker:9092"
}
}- Push notifications when offers match buyer criteria
- Reputation/rating system for sellers
- Automatic price suggestions based on market data
- Bulk offer creation
- Offer editing (currently requires delete + recreate)