diff --git a/Sources/StarCraftCLI/ANSIColor.swift b/Sources/StarCraftCLI/ANSIColor.swift new file mode 100644 index 0000000..8c8aa2e --- /dev/null +++ b/Sources/StarCraftCLI/ANSIColor.swift @@ -0,0 +1,14 @@ +enum ANSIColor { + static let reset = "\u{001B}[0m" + static let bold = "\u{001B}[1m" + + // Race Colors + static let protoss = "\u{001B}[38;2;255;223;0m" // Golden yellow + static let terran = "\u{001B}[38;2;0;156;255m" // Bright blue + static let zerg = "\u{001B}[38;2;163;53;238m" // Purple + + // Accent Colors + static let neon = "\u{001B}[38;2;0;255;255m" // Cyan + static let warning = "\u{001B}[38;2;255;69;0m" // Red-Orange + static let success = "\u{001B}[38;2;50;205;50m" // Lime Green +} diff --git a/Sources/StarCraftCLI/ActivePlayersCommand.swift b/Sources/StarCraftCLI/ActivePlayersCommand.swift new file mode 100644 index 0000000..2a46dce --- /dev/null +++ b/Sources/StarCraftCLI/ActivePlayersCommand.swift @@ -0,0 +1,18 @@ +import Foundation +import StarCraftKit + +struct ActivePlayersCommand: Command { + let api: StarCraftAPI + + var description: String { + return "Fetch active players" + } + + func execute() async throws { + let response = try await api.allPlayers().activePlayers + print("\(ANSIColor.terran)Active Players:\(ANSIColor.reset)") + response.forEach { player in + print("\(ANSIColor.neon)➤\(ANSIColor.reset) \(player.name)") + } + } +} diff --git a/Sources/StarCraftCLI/Command.swift b/Sources/StarCraftCLI/Command.swift new file mode 100644 index 0000000..05f8190 --- /dev/null +++ b/Sources/StarCraftCLI/Command.swift @@ -0,0 +1,4 @@ +protocol Command { + var description: String { get } + func execute() async throws +} diff --git a/Sources/StarCraftCLI/LiveTournamentsCommand.swift b/Sources/StarCraftCLI/LiveTournamentsCommand.swift new file mode 100644 index 0000000..544c0f9 --- /dev/null +++ b/Sources/StarCraftCLI/LiveTournamentsCommand.swift @@ -0,0 +1,16 @@ +import Foundation +import StarCraftKit + +struct LiveTournamentsCommand: Command { + let api: StarCraftAPI + + var description: String { + return "Fetch tournaments with live support" + } + + func execute() async throws { + let response = try await api.allTournaments() + let liveSupported = response.liveSupportedTournaments + print("\(ANSIColor.protoss)Live Supported Tournaments: \(liveSupported.count)\(ANSIColor.reset)") + } +} diff --git a/Sources/StarCraftCLI/OngoingMatchesCommand.swift b/Sources/StarCraftCLI/OngoingMatchesCommand.swift new file mode 100644 index 0000000..d689a68 --- /dev/null +++ b/Sources/StarCraftCLI/OngoingMatchesCommand.swift @@ -0,0 +1,16 @@ +import Foundation +import StarCraftKit + +struct OngoingMatchesCommand: Command { + let api: StarCraftAPI + + var description: String { + return "Fetch ongoing matches" + } + + func execute() async throws { + let response = try await api.allMatches() + let ongoingMatches = response.ongoingMatches + print("\(ANSIColor.zerg)Ongoing Matches: \(ongoingMatches.count)\(ANSIColor.reset)") + } +} diff --git a/Sources/StarCraftCLI/OngoingTournamentsCommand.swift b/Sources/StarCraftCLI/OngoingTournamentsCommand.swift new file mode 100644 index 0000000..5273def --- /dev/null +++ b/Sources/StarCraftCLI/OngoingTournamentsCommand.swift @@ -0,0 +1,16 @@ +import Foundation +import StarCraftKit + +struct OngoingTournamentsCommand: Command { + let api: StarCraftAPI + + var description: String { + return "Fetch ongoing tournaments" + } + + func execute() async throws { + let response = try await api.allTournaments() + let ongoing = response.ongoingTournaments + print("\(ANSIColor.protoss)Ongoing Tournaments: \(ongoing.count)\(ANSIColor.reset)") + } +} diff --git a/Sources/StarCraftCLI/StarCraftCLI.swift b/Sources/StarCraftCLI/StarCraftCLI.swift new file mode 100644 index 0000000..79aafb2 --- /dev/null +++ b/Sources/StarCraftCLI/StarCraftCLI.swift @@ -0,0 +1,75 @@ +import Foundation +import StarCraftKit + +@main +struct StarCraftCLI { + private let api: StarCraftAPI + private var commands: [String: Command] = [:] + + init() { + guard let token = ProcessInfo.processInfo.environment["PANDA_TOKEN"] else { + print("\(ANSIColor.warning)Error: PANDA_TOKEN environment variable not set\(ANSIColor.reset)") + exit(1) + } + + self.api = StarCraftAPI(token: token) + setupCommands() + } + + private mutating func setupCommands() { + commands = [ + "-ap": ActivePlayersCommand(api: api), + "-lt": LiveTournamentsCommand(api: api), + "-om": OngoingMatchesCommand(api: api), + "-ot": OngoingTournamentsCommand(api: api), + "-ut": UpcomingTournamentsCommand(api: api) + ] + } + + static func main() { + let cli = StarCraftCLI() + let arguments = CommandLine.arguments + + if arguments.count > 1 { + cli.execute(command: arguments[1]) + } else { + cli.printInstructions() + } + } + + private func execute(command: String) { + guard let cmd = commands[command] else { + print("\(ANSIColor.warning)Unknown command: \(command)\(ANSIColor.reset)") + return + } + + Task.detached(priority: .medium) { + do { + try await cmd.execute() + exit(0) + } catch { + print("\(ANSIColor.warning)Error: \(error)\(ANSIColor.reset)") + exit(1) + } + } + + RunLoop.current.run() + } + + private func printInstructions() { + print() + print("\(ANSIColor.neon)\(ANSIColor.bold)╔══════════════════════════════════╗") + print("║ Welcome to StarCraftCLI II ║") + print("╚══════════════════════════════════╝\(ANSIColor.reset)") + print() + print("\(ANSIColor.terran)Available Commands:\(ANSIColor.reset)") + commands.forEach { cmd, implementation in + print("\(ANSIColor.protoss)\(cmd)\(ANSIColor.reset) \(implementation.description)") + } + print() + print("\(ANSIColor.warning)Note: You must provide a valid PANDA_TOKEN environment variable\(ANSIColor.reset)") + print() + print("\(ANSIColor.neon)Usage:\(ANSIColor.reset) swift run StarCraftCLI ") + print("\(ANSIColor.neon)Example:\(ANSIColor.reset) swift run StarCraftCLI -ap") + } +} diff --git a/Sources/StarCraftCLI/UpcomingTournamentsCommand.swift b/Sources/StarCraftCLI/UpcomingTournamentsCommand.swift new file mode 100644 index 0000000..d5cc070 --- /dev/null +++ b/Sources/StarCraftCLI/UpcomingTournamentsCommand.swift @@ -0,0 +1,42 @@ +import Foundation +import StarCraftKit + +struct UpcomingTournamentsCommand: Command { + let api: StarCraftAPI + + var description: String { + return "Fetch upcoming tournaments" + } + + func execute() async throws { + let response = try await api.allTournaments() + let upcoming = response.upcomingTournaments + + print("\(ANSIColor.protoss)Upcoming Tournaments:\(ANSIColor.reset)\n") + + for tournament in upcoming { + print("\(ANSIColor.neon)═══════════════════════════════════════\(ANSIColor.reset)") + print("\(ANSIColor.terran)Name:\(ANSIColor.reset) \(tournament.name)") + print("\(ANSIColor.terran)Start Date:\(ANSIColor.reset) \(DateFormatter.prettyFormatter.string(from: tournament.beginAt))") + print("\(ANSIColor.terran)End Date:\(ANSIColor.reset) \(DateFormatter.prettyFormatter.string(from: tournament.endAt))") + print("\(ANSIColor.terran)League:\(ANSIColor.reset) \(tournament.league?.name ?? "Unknown")") + print("\(ANSIColor.terran)Game:\(ANSIColor.reset) \(tournament.videogame?.name ?? "Unknown")") + print("\(ANSIColor.terran)Prize Pool:\(ANSIColor.reset) \(tournament.prizepool ?? "Unknown")") + + if let matches = tournament.matches { + print("\n\(ANSIColor.zerg)Matches:\(ANSIColor.reset)") + for match in matches { + print("\(ANSIColor.neon) ➤\(ANSIColor.reset) \(match.name)") + print(" Status: \(match.status)") + if let opponents = match.opponents { + print(" \(ANSIColor.protoss)VS\(ANSIColor.reset)") + for opponent in opponents { + print(" \(ANSIColor.terran)◆\(ANSIColor.reset) \(opponent.opponent.name)") + } + } + } + } + print() + } + } +} diff --git a/Sources/StarCraftCLI/activePlayers.swift b/Sources/StarCraftCLI/activePlayers.swift deleted file mode 100644 index d501c2d..0000000 --- a/Sources/StarCraftCLI/activePlayers.swift +++ /dev/null @@ -1,10 +0,0 @@ -import Foundation - -func activePlayers() async { - do { - let response = try await api.allPlayers().activePlayers - print("Fetched active players:\n\(response.map(\.name))") - } catch { - print("Failed to fetch active players \(error)") - } -} diff --git a/Sources/StarCraftCLI/liveSupportedGames.swift b/Sources/StarCraftCLI/liveSupportedGames.swift deleted file mode 100644 index f5dad93..0000000 --- a/Sources/StarCraftCLI/liveSupportedGames.swift +++ /dev/null @@ -1,11 +0,0 @@ -import Foundation - -func liveSupportedTournaments() async { - do { - let response = try await api.allTournaments() - let liveSupported = response.liveSupportedTournaments - print("Fetched \(liveSupported.count) tournaments with live support") - } catch { - print("Failed to fetch live supported tournaments: \(error)") - } -} diff --git a/Sources/StarCraftCLI/main.swift b/Sources/StarCraftCLI/main.swift deleted file mode 100644 index 59787f5..0000000 --- a/Sources/StarCraftCLI/main.swift +++ /dev/null @@ -1,38 +0,0 @@ -import Foundation -import StarCraftKit - -let arguments = CommandLine.arguments - -func main() { - guard arguments.count > 1 else { - printInstructions() - return - } - - let command = arguments[1] - - Task.detached(priority: .medium) { - switch command { - case "-ap": - await activePlayers() - case "-ot": - await ongoingTournaments() - case "-ut": - await upcomingTournaments() - case "-lt": - await liveSupportedTournaments() - case "-om": - await ongoingMatches() - default: - print("Unknown command: \(command)") - } - } - - RunLoop.current.run() -} - -let token = ProcessInfo.processInfo.environment["PANDA_TOKEN"]! - -let api = StarCraftAPI(token: token) - -main() diff --git a/Sources/StarCraftCLI/ongoingMatches.swift b/Sources/StarCraftCLI/ongoingMatches.swift deleted file mode 100644 index fde2b5b..0000000 --- a/Sources/StarCraftCLI/ongoingMatches.swift +++ /dev/null @@ -1,11 +0,0 @@ -import Foundation - -func ongoingMatches() async { - do { - let response = try await api.allMatches() - let ongoingMatches = response.ongoingMatches - print("Fetched \(ongoingMatches.count) ongoing matches") - } catch { - print("Failed to fetch ongoing matches: \(error)") - } -} diff --git a/Sources/StarCraftCLI/ongoingTournaments.swift b/Sources/StarCraftCLI/ongoingTournaments.swift deleted file mode 100644 index 013321d..0000000 --- a/Sources/StarCraftCLI/ongoingTournaments.swift +++ /dev/null @@ -1,11 +0,0 @@ -import Foundation - -func ongoingTournaments() async { - do { - let response = try await api.allTournaments() - let ongoing = response.ongoingTournaments - print("Fetched \(ongoing.count) ongoing tournaments") - } catch { - print("Failed to fetch ongoing tournaments: \(error)") - } -} diff --git a/Sources/StarCraftCLI/printInstructions.swift b/Sources/StarCraftCLI/printInstructions.swift deleted file mode 100644 index ce0f4ef..0000000 --- a/Sources/StarCraftCLI/printInstructions.swift +++ /dev/null @@ -1,19 +0,0 @@ -import Foundation - -func printInstructions() { - print() - print("👾 Welcome to StarCraftCLI 👾") - print() - print("Commands:") - print("-m all matches") - print("-ot ongoing tournaments") - print("-ut upcoming tournaments") - print("-lt live supported tournaments") - print("-om ongoing matches") - print() - print("You must provide a valid PANDA_TOKEN environment variable") - print() - print("Usage: swift build, swift run StarCraftCLI ") - print("Example: swift run StarCraftCLI -t. This will fetch all tournaments") -} - diff --git a/Sources/StarCraftCLI/upcomingTournaments.swift b/Sources/StarCraftCLI/upcomingTournaments.swift deleted file mode 100644 index 24380d0..0000000 --- a/Sources/StarCraftCLI/upcomingTournaments.swift +++ /dev/null @@ -1,65 +0,0 @@ -import Foundation - -func upcomingTournaments() async { - do { - let response = try await api.allTournaments() - let upcoming = response.upcomingTournaments - - let formattedTournaments = upcoming.map { tournament in - let matchesInfo = tournament.matches?.map { match in - """ - Match Name: \(match.name) - Match ID: \(match.id ?? 0) - Status: \(match.status) - Scheduled At: \(match.scheduledAt.map { DateFormatter.prettyFormatter.string(from: $0) } ?? "Unknown") - Begin At: \(match.beginAt.map { DateFormatter.prettyFormatter.string(from: $0) } ?? "Unknown") - End At: \(match.endAt.map { DateFormatter.prettyFormatter.string(from: $0) } ?? "Unknown") - Number of Games: \(match.numberOfGames) - Match Type: \(match.matchType) - Detailed Stats: \(match.detailedStats) - Live Supported: \(match.live.supported) - Draw: \(match.draw) - Forfeit: \(match.forfeit) - Rescheduled: \(match.rescheduled) - Game Advantage: \(match.gameAdvantage ?? 0) - Winner ID: \(match.winnerId ?? 0) - Winner Type: \(match.winnerType ?? "Unknown") - Opponents: \(match.opponents?.map { "\($0.opponent.name) (\($0.type))" }.joined(separator: ", ") ?? "Unknown") - Streams: \(match.streamsList.compactMap(\.embedUrl)) - \n - """ - }.joined(separator: "\n\n") ?? "No Matches" - - return """ - Name: \(tournament.name) - Start Date: \(DateFormatter.prettyFormatter.string(from: tournament.beginAt)) - End Date: \(DateFormatter.prettyFormatter.string(from: tournament.endAt)) - League: \(tournament.league?.name ?? "Unknown") - League Image: \(tournament.league?.imageUrl?.absoluteString ?? "–") - League URL: \(tournament.league?.url?.absoluteString ?? "–") - League ID: \(tournament.leagueId) - Game: \(tournament.videogame?.name ?? "Unknown") - Game Slug: \(tournament.videogame?.slug ?? "Unknown") - Tier: \(tournament.tier) - Slug: \(tournament.slug) - Has Bracket: \(tournament.hasBracket) - Live Supported: \(tournament.liveSupported) - Detailed Stats: \(tournament.detailedStats) - Prize Pool: \(tournament.prizepool ?? "Unknown") - Series: \(tournament.serie?.name ?? "Unknown") - Series ID: \(tournament.serieId) - Winner ID: \(tournament.winnerId ?? 0) - Winner Type: \(tournament.winnerType ?? "Unknown") - Match Count: \(tournament.matches?.count ?? 0) - Matches: \(matchesInfo) - Last Modified: \(DateFormatter.prettyFormatter.string(from: tournament.modifiedAt)) - ID: \(tournament.id) - \n - """ - } - - formattedTournaments.forEach { print($0) } - } catch { - print("Failed to fetch upcoming tournaments: \(error)") - } -}