Skip to content

Commit

Permalink
Merge pull request #2 from VladimirCreator/master-development
Browse files Browse the repository at this point in the history
thisusernameisalreadybusy/master-development
  • Loading branch information
VladimirCreator authored Oct 1, 2023
2 parents 8f98f5d + ec6d477 commit c73dbe6
Show file tree
Hide file tree
Showing 51 changed files with 5,126 additions and 85 deletions.
95 changes: 10 additions & 85 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,90 +1,15 @@
# Xcode
#
# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
# The Swift Package Manager

## User settings
xcuserdata/
## Folder
.build

## compatibility with Xcode 8 and earlier (ignoring not required starting Xcode 9)
*.xcscmblueprint
*.xccheckout
## File
Package.resolved

## compatibility with Xcode 3 and earlier (ignoring not required starting Xcode 4)
build/
DerivedData/
*.moved-aside
*.pbxuser
!default.pbxuser
*.mode1v3
!default.mode1v3
*.mode2v3
!default.mode2v3
*.perspectivev3
!default.perspectivev3
# The Node Package Manager

## Obj-C/Swift specific
*.hmap
## Folder
dist
node_modules

## App packaging
*.ipa
*.dSYM.zip
*.dSYM

## Playgrounds
timeline.xctimeline
playground.xcworkspace

# Swift Package Manager
#
# Add this line if you want to avoid checking in source code from Swift Package Manager dependencies.
# Packages/
# Package.pins
# Package.resolved
# *.xcodeproj
#
# Xcode automatically generates this directory with a .xcworkspacedata file and xcuserdata
# hence it is not needed unless you have added a package configuration file to your project
# .swiftpm

.build/

# CocoaPods
#
# We recommend against adding the Pods directory to your .gitignore. However
# you should judge for yourself, the pros and cons are mentioned at:
# https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
#
# Pods/
#
# Add this line if you want to avoid checking in source code from the Xcode workspace
# *.xcworkspace

# Carthage
#
# Add this line if you want to avoid checking in source code from Carthage dependencies.
# Carthage/Checkouts

Carthage/Build/

# Accio dependency management
Dependencies/
.accio/

# fastlane
#
# It is recommended to not store the screenshots in the git repo.
# Instead, use fastlane to re-generate the screenshots whenever they are needed.
# For more information about the recommended setup visit:
# https://docs.fastlane.tools/best-practices/source-control/#source-control

fastlane/report.xml
fastlane/Preview.html
fastlane/screenshots/**/*.png
fastlane/test_output

# Code Injection
#
# After new code Injection tools there's a generated folder /iOSInjectionProject
# https://github.com/johnno1962/injectionforxcode

iOSInjectionProject/
## File
19 changes: 19 additions & 0 deletions backend/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
FROM swift as build
WORKDIR /app

COPY Package.swift .
COPY Sources ./Sources

RUN swift build --configuration release


FROM swift as production

WORKDIR /app

ENV COMPILER_BOT_TOKEN=
ENV IHaveNotComeUpWithAName=

COPY --from=build /app/.build .

CMD ["/app/x86_64-unknown-linux-gnu/release/Bot"]
20 changes: 20 additions & 0 deletions backend/Package.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// swift-tools-version:5.8
import PackageDescription

fileprivate let package: Package = .init(
name: "Bot",
dependencies: [
.package(url: "https://github.com/apple/swift-docc-plugin", from: "1.0.0"),
.package(url: "https://github.com/vapor/vapor.git", from: "4.76.0"),
.package(url: "https://github.com/nerzh/telegram-vapor-bot", from: "2.1.0")
],
targets: [
.executableTarget(
name: "Bot",
dependencies: [
.product(name: "Vapor", package: "vapor"),
.product(name: "TelegramVaporBot", package: "telegram-vapor-bot")
]
)
]
)
14 changes: 14 additions & 0 deletions backend/Sources/Bot/Actor/TGBotConnection.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import Foundation
import TelegramVaporBot

internal actor TGBotConnection {
private var _connection: TGConnectionPrtcl!

internal var connection: TGConnectionPrtcl {
get { return _connection }
}

internal func setConnection(_ connection: TGConnectionPrtcl) {
_connection = connection
}
}
9 changes: 9 additions & 0 deletions backend/Sources/Bot/Button/TGCompileButton.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import Vapor
import TelegramVaporBot

internal let tgCompileButton: TGKeyboardButton = .init(
text: "/compile",
webApp: .init(
url: Environment.get("IHaveNotComeUpWithAName")!
)
)
7 changes: 7 additions & 0 deletions backend/Sources/Bot/Button/TGKeyboardButton.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import TelegramVaporBot

internal let tgKeyboardButton: [[TGKeyboardButton]] = [
[
tgCompileButton
]
]
18 changes: 18 additions & 0 deletions backend/Sources/Bot/Controller/TelegramController.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import Vapor
import TelegramVaporBot

internal final class TelegramController: RouteCollection {
internal func boot(routes: RoutesBuilder) throws {
routes.post("telegramWebHook", use: telegramWebHook)
}
}

internal extension TelegramController {
internal func telegramWebHook(_ request: Request) async throws -> Bool {
let update: TGUpdate = try request.content.decode(TGUpdate.self)

return try await connection.connection.dispatcher.process(
[update]
)
}
}
7 changes: 7 additions & 0 deletions backend/Sources/Bot/Handler/AttachHandlers.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import Vapor
import TelegramVaporBot

internal func attachHandlers(for application: Application, using connection: TGConnectionPrtcl) async -> Void {
await connection.dispatcher.add(startHandler)
await connection.dispatcher.add(compileHandler)
}
56 changes: 56 additions & 0 deletions backend/Sources/Bot/Handler/Collection/CompilerHandler.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import Foundation
import TelegramVaporBot

fileprivate struct SwiftWebAppData: Decodable { // Initially Modified: 02:51 AM Sun 01 Oct 2023
private enum JSONKeys: String, CodingKey {
case data
}

fileprivate let data: Recipe

fileprivate init(from decoder: Decoder) throws {
let values = try decoder.container(keyedBy: JSONKeys.self)

guard let data = try? values.decode(Recipe.self, forKey: .data) else {
fatalError("\(#function)")
}

self.data = data
}
}

internal let compileHandler: TGBaseHandler = .init() { update, bot in
guard let message = update.message else { return }
guard let data = message.webAppData?.data.data(using: .utf8) else { return }

let decoder: JSONDecoder = .init()
guard let recipe = try? decoder.decode(SwiftWebAppData.self, from: data).data else { return }

let cppRunner: ProcessRunner = .init(language: "C++", fileURLWithPath: "g++", arguments: ["./recipe.cpp", "-o", "./recipe"], ext: "cpp")
let swiftRunner: ProcessRunner = .init(language: "Swift", fileURLWithPath: "swiftc", arguments: ["./recipe.swift"], ext: "swift")
let javascriptRunner: ProcessRunner = .init(language: "JavaScript", fileURLWithPath: "", arguments: ["not implemented"], ext: "")
let typescriptRunner: ProcessRunner = .init(language: "TypeScript", fileURLWithPath: "", arguments: ["not implemented"], ext: "")

cppRunner.nextRunner = swiftRunner
swiftRunner.nextRunner = javascriptRunner
javascriptRunner.nextRunner = typescriptRunner

let (stdout, stderr): (stdout: String?, stderr: String?) = try cppRunner.execute(recipe)

if let stdin {
try await bot.sendMessage(
params: .init(
chatId: .chat(message.chat.id),
text: "stdout:\n\(stdout)"
)
)
}
else {
try await bot.sendMessage(
params: .init(
chatId: .chat(message.chat.id),
text: "Program does not print anything or an error occured."
)
)
}
}
17 changes: 17 additions & 0 deletions backend/Sources/Bot/Handler/Collection/StartHandler.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import TelegramVaporBot

internal let startHandler: TGCommandHandler = .init(commands: ["start"]) { update, bot in
// Why does this live breaks Swift?
//guard let IHaveNotComeUpWithAName0: String = update.message?.chat.id else { return }
guard let IHaveNotComeUpWithAName0 = update.message?.chat.id else { return }

let IHaveNotComeUpWithAName1: TGSendMessageParams = .init(
chatId: .chat(IHaveNotComeUpWithAName0),
text: "Мне нужно знать что мне компилировать. Нажмите на кнопку, чтобы дать мне инструкции.",
replyMarkup: .replyKeyboardMarkup(
.init(keyboard: tgKeyboardButton)
)
)

try await bot.sendMessage(params: IHaveNotComeUpWithAName1)
}
57 changes: 57 additions & 0 deletions backend/Sources/Bot/IHaveNotComeUpWithAName/ProcessRunner.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import Foundation
import Vapor

internal class ProcessRunner {
private let language: String
private let fileURLWithPath: String
private let arguments: [String]
private let ext: String

internal var nextRunner: ProcessRunner?

internal init(language: String, fileURLWithPath: String, arguments: [String], ext: String) {
self.language = language
self.fileURLWithPath = fileURLWithPath
self.arguments = arguments
self.ext = ext
}

internal func execute(_ recipe: Recipe) throws -> (stdout: String?, stderr: String?) {
guard let nextRunner else { return (stdout: nil, stderr: nil) }

guard language == recipe.language else { return try nextRunner.execute(recipe) }
guard let data = recipe.text.data(using: .utf8) else { return (stdout: nil, stderr: nil) }

guard FileManager.default.createFile(atPath: "./recipe.\(ext)", contents: data) else { return (stdout: nil, stderr: nil) }

let compileProcess: Process = .init()
compileProcess.environment = .init()
compileProcess.environment?["PATH"] = Environment.get("PATH")
compileProcess.arguments = arguments

for path in compileProcess.environment!["PATH"]!.split(separator: ":") {
compileProcess.executableURL = URL(fileURLWithPath: "\(path)/\(fileURLWithPath)")
do {
try compileProcess.run(); compileProcess.waitUntilExit()
}
catch {

}
}

let recipeProcess: Process = .init(); let pipe: Pipe = .init()
recipeProcess.executableURL = URL(fileURLWithPath: "./recipe")
recipeProcess.arguments = recipe.stdin?.split(separator: " ").map { String($0) }
recipeProcess.standardOutput = pipe

try recipeProcess.run(); recipeProcess.waitUntilExit()

if let data = try? pipe.fileHandleForReading.readToEnd(),
let stdout = try? String(data: data, encoding: .utf8) {
return (stdout: stdout, stderr: nil)
}
else {
return (stdout: nil, stderr: nil)
}
}
}
30 changes: 30 additions & 0 deletions backend/Sources/Bot/Model/Recipe.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/* Initially Modified: 09:40 PM Sat 30 Sep 2023
*/

internal struct Recipe {
internal let language: String
internal let text: String
internal let stdin: String?
}

extension Recipe: Decodable { // Initially Modified: 09:41 PM Sat 30 Sep 2023
private enum JSONKeys: String, CodingKey {
case language
case text
case stdin
}

internal init(from decoder: Decoder) throws {
let values = try decoder.container(keyedBy: JSONKeys.self)

guard let language = try? values.decode(String.self, forKey: .language),
let text = try? values.decode(String.self, forKey: .text)
else {
fatalError("\(#function)")
}

self.language = language
self.text = text
self.stdin = try? values.decode(String.self, forKey: .stdin)
}
}
20 changes: 20 additions & 0 deletions backend/Sources/Bot/configure.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import Vapor
import TelegramVaporBot

internal func configure(_ application: Application) async throws -> Void {
guard let compilerBotToken: String = Environment.get("COMPILER_BOT_TOKEN") else {
fatalError("\(#function)")
}

let bot: TGBot = .init(
app: application,
botId: compilerBotToken
)

await connection.setConnection(try await TGLongPollingConnection(bot: bot))
try await connection.connection.start()

await attachHandlers(for: application, using: connection.connection)

try routes(application)
}
Empty file.
11 changes: 11 additions & 0 deletions backend/Sources/Bot/main.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import Vapor

/* this ⌄ `try` is actually for `.detect()`.
*/
fileprivate let application: Application = try .init(.detect())
internal let connection: TGBotConnection = .init()

try await configure(application)
try application.run()

defer { application.shutdown() }
Loading

0 comments on commit c73dbe6

Please sign in to comment.