From 4e5c2cd756fc163a7a23587ae3d344b441dfa1b0 Mon Sep 17 00:00:00 2001 From: shivang0-0 Date: Fri, 3 May 2024 18:13:47 +0530 Subject: [PATCH 1/2] added: mailing script --- lib/screens/login_screen.dart | 2 +- scripts/mail-scripts/email_body.html | 16 +++++ scripts/mail-scripts/emails.json | 7 ++ scripts/mail-scripts/go.mod | 10 +++ scripts/mail-scripts/go.sum | 12 ++++ scripts/mail-scripts/main.go | 102 +++++++++++++++++++++++++++ scripts/mail-scripts/template.html | 7 ++ 7 files changed, 155 insertions(+), 1 deletion(-) create mode 100644 scripts/mail-scripts/email_body.html create mode 100644 scripts/mail-scripts/emails.json create mode 100644 scripts/mail-scripts/go.mod create mode 100644 scripts/mail-scripts/go.sum create mode 100644 scripts/mail-scripts/main.go create mode 100644 scripts/mail-scripts/template.html diff --git a/lib/screens/login_screen.dart b/lib/screens/login_screen.dart index ee8fb7c..6cfce2d 100644 --- a/lib/screens/login_screen.dart +++ b/lib/screens/login_screen.dart @@ -56,7 +56,7 @@ class _LoginPageState extends State { width: 315, height: 65, decoration: const BoxDecoration( - color: Colors.transparent + color: Colors.black ), ), ), diff --git a/scripts/mail-scripts/email_body.html b/scripts/mail-scripts/email_body.html new file mode 100644 index 0000000..53d0534 --- /dev/null +++ b/scripts/mail-scripts/email_body.html @@ -0,0 +1,16 @@ + + + + + + OSDHACK '24 - AR Code + + +

Ahoy OSDHACK Participants!

+

Thank you for registering for OSDHACK '24. Below is your unique QR code for attendance:

+ AR Code +

Please make sure to bring this code with you to the event.

+

For more information and updates, visit the OSDHACK '24 website.

+

Best regards,
OSDC

+ + diff --git a/scripts/mail-scripts/emails.json b/scripts/mail-scripts/emails.json new file mode 100644 index 0000000..376a285 --- /dev/null +++ b/scripts/mail-scripts/emails.json @@ -0,0 +1,7 @@ +{ + "subscribers": [ + "shivang.sharma2062@gmail.com", + "krishkushjain@gmail.com", + "rahulkalra045@gmail.com" + ] +} \ No newline at end of file diff --git a/scripts/mail-scripts/go.mod b/scripts/mail-scripts/go.mod new file mode 100644 index 0000000..bbc90f8 --- /dev/null +++ b/scripts/mail-scripts/go.mod @@ -0,0 +1,10 @@ +module github.com/osdc/Mercurius + +go 1.15 + +require ( + github.com/yuin/goldmark v1.3.5 + github.com/yuin/goldmark-meta v1.0.0 // indirect + gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect + gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df // indirect +) diff --git a/scripts/mail-scripts/go.sum b/scripts/mail-scripts/go.sum new file mode 100644 index 0000000..8b8a9fe --- /dev/null +++ b/scripts/mail-scripts/go.sum @@ -0,0 +1,12 @@ +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.3.5 h1:dPmz1Snjq0kmkz159iL7S6WzdahUTHnHB5M56WFVifs= +github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +github.com/yuin/goldmark-meta v1.0.0 h1:ScsatUIT2gFS6azqzLGUjgOnELsBOxMXerM3ogdJhAM= +github.com/yuin/goldmark-meta v1.0.0/go.mod h1:zsNNOrZ4nLuyHAJeLQEZcQat8dm70SmB2kHbls092Gc= +gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc h1:2gGKlE2+asNV9m7xrywl36YYNnBG5ZQ0r/BOOxqPpmk= +gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc/go.mod h1:m7x9LTH6d71AHyAX77c9yqWCCa3UKHcVEj9y7hAtKDk= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df h1:n7WqCuqOuCbNr617RXOY0AWRXxgwEyPp2z+p0+hgMuE= +gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df/go.mod h1:LRQQ+SO6ZHR7tOkpBDuZnXENFzX8qRjMDMyPD6BRkCw= +gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/scripts/mail-scripts/main.go b/scripts/mail-scripts/main.go new file mode 100644 index 0000000..1449e5f --- /dev/null +++ b/scripts/mail-scripts/main.go @@ -0,0 +1,102 @@ +package main + +import ( + "bytes" + "encoding/json" + "fmt" + "html" + "html/template" + "io/ioutil" + "log" + "os" + + "github.com/yuin/goldmark" + meta "github.com/yuin/goldmark-meta" + "github.com/yuin/goldmark/parser" + "gopkg.in/gomail.v2" +) + +type subscriberList struct { + Subscribers []string `json:"subscribers"` +} + +func main() { + markdown := goldmark.New( + goldmark.WithExtensions( + meta.Meta, + ), + ) + + jsonFile, err := os.Open("emails.json") + + if err != nil { + log.Print(err) + } + + defer jsonFile.Close() + + byteValue, _ := ioutil.ReadAll(jsonFile) + + var list subscriberList + + json.Unmarshal(byteValue, &list) + + content, _ := ioutil.ReadFile("../../content/post/example.md") + + var buf bytes.Buffer + context := parser.NewContext() + if err := markdown.Convert(content, &buf, parser.WithContext(context)); err != nil { + panic(err) + } + metaData := meta.Get(context) + title := metaData["title"] + str := fmt.Sprintf("%v", title) + + t := template.New("template.html") + t, _ = t.ParseFiles("template.html") + + var body bytes.Buffer + + if err := t.Execute(&body, struct { + Content string + Title string + }{ + Content: buf.String(), + Title: str, + }); err != nil { + log.Println(err) + } + html := html.UnescapeString(body.String()) + + send(html, list.Subscribers) +} + +func send(body string, to []string) { + from := os.Getenv("MAIL_ID") + pass := os.Getenv("MAIL_PASSWORD") + + d := gomail.NewDialer("smtp.gmail.com", 587, from, pass) + s, err := d.Dial() + if err != nil { + panic(err) + } + + bodyContent, err := ioutil.ReadFile("email_body.html") + if err != nil { + log.Fatal(err) + } + + m := gomail.NewMessage() + for _, r := range to { + fmt.Printf("Sending email to: %s\n", r) + m.SetHeader("From", from) + m.SetAddressHeader("To", r, r) + m.SetHeader("Subject", "OSDHACK '24 Attendance QR") + m.SetBody("text/html", string(bodyContent)) + + if err := gomail.Send(s, m); err != nil { + log.Printf("Could not send email to %q: %v", r, err) + } + m.Reset() + } +} diff --git a/scripts/mail-scripts/template.html b/scripts/mail-scripts/template.html new file mode 100644 index 0000000..1a3c84d --- /dev/null +++ b/scripts/mail-scripts/template.html @@ -0,0 +1,7 @@ + + + +

{{.Title}}

+

{{.Content}}

+ + From 51ef28c3b8c003354d92f917522f23e39a9f80bd Mon Sep 17 00:00:00 2001 From: shivang0-0 Date: Wed, 8 May 2024 00:19:06 +0530 Subject: [PATCH 2/2] modified: app flow, introdued: db --- .gitignore | 4 + lib/screens/attendance_screen.dart | 232 ++++++++++++++++++---- lib/screens/id_screen.dart | 201 ++++++++++++++----- lib/screens/login_screen.dart | 147 +++++++++----- lib/screens/qr_screen.dart | 297 +++++++++++++++++++++------- lib/screens/success_screen.dart | 167 +++++++++++++--- lib/services/postGres_services.dart | 149 ++++++++++++++ lib/widgets/submit_button.dart | 36 ++++ lib/widgets/text_field_input.dart | 49 +++++ scripts/mail-scripts/emails.json | 8 +- scripts/mail-scripts/go.mod | 4 +- scripts/mail-scripts/go.sum | 46 +++++ scripts/mail-scripts/main.go | 54 ++++- 13 files changed, 1155 insertions(+), 239 deletions(-) create mode 100644 lib/services/postGres_services.dart create mode 100644 lib/widgets/submit_button.dart create mode 100644 lib/widgets/text_field_input.dart diff --git a/.gitignore b/.gitignore index 6503b5e..598d35d 100644 --- a/.gitignore +++ b/.gitignore @@ -41,3 +41,7 @@ app.*.map.json /android/app/debug /android/app/profile /android/app/release + +#emails and qrs +/scripts/mail-scripts/qrcodes +/scripts/mail-scripts/emails.json \ No newline at end of file diff --git a/lib/screens/attendance_screen.dart b/lib/screens/attendance_screen.dart index ebd5bae..ae45aae 100644 --- a/lib/screens/attendance_screen.dart +++ b/lib/screens/attendance_screen.dart @@ -1,63 +1,221 @@ import 'package:attendx/screens/id_screen.dart'; +import 'package:attendx/screens/qr_screen.dart'; import 'package:attendx/screens/success_screen.dart'; +import 'package:attendx/services/postGres_services.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; class AttendanceScreen extends StatefulWidget { - const AttendanceScreen({Key? key}) : super(key: key); + final String code; + const AttendanceScreen({ + Key? key, + required this.code, + }) : super(key: key); @override State createState() => _AttendanceScreenState(); } class _AttendanceScreenState extends State { + int selectedDay = 1; + bool isLoading = false; + + markAttendance() async { + setState(() { + isLoading = true; + }); + print("Selected day: $selectedDay"); + selectedDay == 1 + ? await markAttendance1(widget.code) + : selectedDay == 2 + ? await markAttendance2(widget.code) + : await markAttendance3(widget.code); + + Navigator.of(context).pushReplacement(MaterialPageRoute( + builder: (_) => SuccessScreen( + day: selectedDay, + ), + )); + } + @override Widget build(BuildContext context) { return Scaffold( - body: Stack( + backgroundColor: bgColor, + appBar: AppBar( + leading: IconButton( + onPressed: () { + Navigator.of(context).pushReplacement(MaterialPageRoute( + builder: (_) => const QrScreen(), + )); + }, + icon: const Icon( + Icons.arrow_back_ios_new_rounded, + color: Colors.black87, + ), + ), + centerTitle: true, + title: const Text( + "AttendX", + style: TextStyle( + color: Colors.black87, + fontSize: 24, + fontWeight: FontWeight.bold, + letterSpacing: 1, + ), + ), + ), + body: Column( children: [ - Container( - height: MediaQuery.of(context).size.height, - width: MediaQuery.of(context).size.width, - decoration: const BoxDecoration( - image: DecorationImage( - image: AssetImage("assets/attendance.png"), - fit: BoxFit.cover, + const SizedBox( + height: 20, + ), + const Center( + child: Text( + "Choose Slot for attendance", + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 40, + fontWeight: FontWeight.bold, + color: Colors.black87, ), ), ), - Positioned( - bottom: 100, - right: 55, - child: InkWell( - onTap: () { - Navigator.of(context).pushReplacement(MaterialPageRoute( - builder: (_) => const SuccessScreen(), - )); - }, - child: Container( - width: 250, - height: 50, - decoration: const BoxDecoration( - color: Colors.transparent, + const SizedBox( + height: 20, + ), + Padding( + padding: const EdgeInsets.all(8.0), + child: Container( + decoration: const ShapeDecoration( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.all( + Radius.circular(20), + ), ), + color: Color.fromARGB(255, 235, 230, 230), + ), + child: Column( + children: [ + const SizedBox( + height: 20, + ), + InkWell( + onTap: () { + setState(() { + selectedDay = 1; + }); + }, + child: Container( + height: 40, + width: MediaQuery.of(context).size.width, + decoration: BoxDecoration( + color: selectedDay == 1 + ? const Color.fromARGB(255, 34, 122, 88) + : const Color.fromARGB(255, 235, 230, 230), + ), + child: Text( + "Day 1", + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 30, + color: selectedDay == 1 ? Colors.white : Colors.black, + ), + ), + ), + ), + const SizedBox( + height: 10, + ), + // const Divider(), + InkWell( + onTap: () { + setState(() { + selectedDay = 2; + }); + }, + child: Container( + height: 40, + width: MediaQuery.of(context).size.width, + decoration: BoxDecoration( + color: selectedDay == 2 + ? const Color.fromARGB(255, 34, 122, 88) + : const Color.fromARGB(255, 235, 230, 230), + ), + child: Text( + "Day 2", + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 30, + color: + selectedDay == 2 ? Colors.white : Colors.black), + ), + ), + ), + // const Divider(), + const SizedBox( + height: 10, + ), + InkWell( + onTap: () { + setState(() { + selectedDay = 3; + }); + }, + child: Container( + height: 40, + width: MediaQuery.of(context).size.width, + decoration: BoxDecoration( + color: selectedDay == 3 + ? const Color.fromARGB(255, 34, 122, 88) + : const Color.fromARGB(255, 235, 230, 230), + ), + child: Text( + "Day 3", + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 30, + color: + selectedDay == 3 ? Colors.white : Colors.black), + ), + ), + ), + const SizedBox( + height: 20, + ), + ], ), ), ), - Positioned( - bottom: 20, - left: 20, - child: InkWell( - onTap: () { - Navigator.of(context).pushReplacement(MaterialPageRoute( - builder: (_) => const IdScreen(), - )); - }, - child: Container( - width: 100, - height: 50, - decoration: const BoxDecoration( - color: Colors.transparent, + const SizedBox( + height: 40, + ), + isLoading + ? const CircularProgressIndicator( + color: Color.fromARGB(255, 34, 122, 88), + ) + : Container(), + const SizedBox( + height: 20, + ), + InkWell( + onTap: markAttendance, + child: Container( + width: 200, + height: 70, + decoration: const BoxDecoration( + color: Color.fromARGB(255, 34, 122, 88), + borderRadius: BorderRadius.all(Radius.circular(12)), + ), + child: const Center( + child: Text( + "Mark Attendance", + textAlign: TextAlign.center, + style: TextStyle( + color: Colors.white, + fontSize: 24, + fontWeight: FontWeight.bold, + ), ), ), ), diff --git a/lib/screens/id_screen.dart b/lib/screens/id_screen.dart index 0c301c1..586c399 100644 --- a/lib/screens/id_screen.dart +++ b/lib/screens/id_screen.dart @@ -1,68 +1,179 @@ import 'package:attendx/screens/attendance_screen.dart'; import 'package:attendx/screens/qr_screen.dart'; +import 'package:attendx/screens/success_screen.dart'; +import 'package:attendx/services/postGres_services.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; +import 'package:qr_flutter/qr_flutter.dart'; class IdScreen extends StatefulWidget { - const IdScreen({Key? key}) : super(key: key); + final String code; + final Function() closeScreen; + const IdScreen({ + Key? key, + required this.code, + required this.closeScreen, + }) : super(key: key); @override State createState() => _IdScreenState(); } class _IdScreenState extends State { + String imageURL = ""; + + Future sendCodeToPostgres(String code) async { + String fileId = ""; + final string = await sendCodeToPost(code); + print(string); + final splitStrings = string.split('='); + if (splitStrings.length > 1) { + setState(() { + fileId = splitStrings[1]; + imageURL = "https://drive.google.com/uc?export=view&id=$fileId"; + print(imageURL); + }); + } else { + setState(() { + fileId = ""; + }); + } + } + + @override + void initState() { + super.initState(); + sendCodeToPostgres(widget.code); + } + @override Widget build(BuildContext context) { return Scaffold( - body: Stack( - children: [ - Container( - height: MediaQuery.of(context).size.height, - width: MediaQuery.of(context).size.width, - decoration: const BoxDecoration( - image: DecorationImage( - image: AssetImage("assets/idCard.png"), - fit: BoxFit.cover, + backgroundColor: bgColor, + appBar: AppBar( + leading: IconButton( + onPressed: () { + widget.closeScreen(); + Navigator.pushReplacement( + context, + MaterialPageRoute( + builder: (context) => const QrScreen(), ), - ), + ); + }, + icon: const Icon( + Icons.arrow_back_ios_new_rounded, + color: Colors.black87, ), - Positioned( - bottom: 170, - right: 90, - child: InkWell( - onTap: () { - Navigator.of(context).pushReplacement(MaterialPageRoute( - builder: (_) => const AttendanceScreen(), - )); - }, - child: Container( - width: 170, - height: 50, - decoration: const BoxDecoration( - color: Colors.transparent, - ), - ), - ), + ), + centerTitle: true, + title: const Text( + "AttendX", + style: TextStyle( + color: Colors.black87, + fontSize: 24, + fontWeight: FontWeight.bold, + letterSpacing: 1, ), - Positioned( - bottom: 20, - left: 20, - child: InkWell( - onTap: () { - Navigator.of(context).pushReplacement(MaterialPageRoute( - builder: (_) => const QrScreen(), - )); - }, - child: Container( - width: 100, - height: 50, - decoration: const BoxDecoration( - color: Colors.transparent, - ), + ), + ), + body: Container( + alignment: Alignment.center, + padding: const EdgeInsets.all(12.0), + child: Column( + children: [ + // QrImageView( + // data: "", + // size: 150, + // version: QrVersions.auto, // This will be set dynamically + // ), + const Text( + "Participant ID", + style: TextStyle( + color: Colors.black54, + fontSize: 20, + fontWeight: FontWeight.bold, + letterSpacing: 1, ), ), - ), - ], + const SizedBox( + height: 20, + ), + imageURL.isEmpty + ? const CircularProgressIndicator() + : Padding( + padding: const EdgeInsets.all(8.0), + child: Image( + image: NetworkImage( + imageURL, + ), + ), + ), + const SizedBox( + height: 30, + ), + Padding( + padding: const EdgeInsets.all(8.0), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + InkWell( + onTap: () { + connectWithPost(); + Navigator.of(context).pushReplacement(MaterialPageRoute( + builder: (_) => AttendanceScreen(code: widget.code,), + )); + }, + child: Container( + width: 120, + height: 60, + decoration: const BoxDecoration( + color: Colors.green, + borderRadius: BorderRadius.all(Radius.circular(12)), + ), + child: const Center( + child: Text( + "Approve", + style: TextStyle( + color: Colors.white, + fontSize: 22, + fontWeight: FontWeight.bold, + ), + ), + ), + ), + ), + InkWell( + onTap: () { + connectWithPost(); + Navigator.of(context).pushReplacement(MaterialPageRoute( + builder: (_) => const QrScreen(), + )); + }, + child: Container( + width: 120, + height: 60, + decoration: const BoxDecoration( + color: Colors.red, + borderRadius: BorderRadius.all(Radius.circular(12)), + ), + child: const Center( + child: Text( + "Decline", + style: TextStyle( + color: Colors.white, + fontSize: 22, + fontWeight: FontWeight.bold, + ), + ), + ), + ), + ), + ], + ), + ) + ], + ), ), ); } diff --git a/lib/screens/login_screen.dart b/lib/screens/login_screen.dart index 6cfce2d..a5f0c88 100644 --- a/lib/screens/login_screen.dart +++ b/lib/screens/login_screen.dart @@ -1,67 +1,124 @@ import 'package:attendx/screens/qr_screen.dart'; +import 'package:attendx/services/postGres_services.dart'; +import 'package:attendx/widgets/submit_button.dart'; +import 'package:attendx/widgets/text_field_input.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; +import 'package:line_awesome_flutter/line_awesome_flutter.dart'; class LoginPage extends StatefulWidget { - const LoginPage({Key? key, }) : super(key: key); + const LoginPage({ + Key? key, + }) : super(key: key); @override State createState() => _LoginPageState(); } class _LoginPageState extends State { - // @override - // void initState() { - // super.initState(); - // SystemChrome.setEnabledSystemUIMode(SystemUiMode.immersive); + final TextEditingController emailController = TextEditingController(); + final TextEditingController passwordController = TextEditingController(); + bool isLoading = false; - // Future.delayed(const Duration(seconds: 8), () { - // Navigator.of(context).pushReplacement(MaterialPageRoute( - // builder: (_) => const QrScreen(), - // )); - // }); - // } + void signInUser() { + if (emailController.text.isEmpty) { + showDialog( + context: context, + builder: (BuildContext context) => const AlertDialog( + title: Text("Error!"), + content: Text("Please enter your Email."), + ), + ); + } else if (passwordController.text.isEmpty) { + showDialog( + context: context, + builder: (BuildContext context) => const AlertDialog( + title: Text("Error!"), + content: Text("Please enter your password."), + ), + ); + } else { + setState(() { + isLoading = true; + }); + connectWithPost(); + SystemChrome.setEnabledSystemUIMode(SystemUiMode.immersive); + Future.delayed(const Duration(seconds: 4), () { + Navigator.of(context).pushReplacement(MaterialPageRoute( + builder: (_) => const QrScreen(), + )); + }); + } + } + + @override + void dispose() { + SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual, + overlays: SystemUiOverlay.values); + super.dispose(); + emailController.dispose(); + passwordController.dispose(); + } + + @override + void initState() { + super.initState(); + } - // @override - // void dispose() { - // SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual, - // overlays: SystemUiOverlay.values); - // super.dispose(); - // } @override Widget build(BuildContext context) { return Scaffold( - body: Stack( - children: [ - Container( - height: MediaQuery.of(context).size.height, - width: MediaQuery.of(context).size.width, - decoration: const BoxDecoration( - image: DecorationImage( - image: AssetImage("assets/login.png"), - fit: BoxFit.cover, - ), - ), - ), - Positioned( - bottom: 130, - right: 20, - child: InkWell( - onTap: () { - Navigator.of(context).pushReplacement(MaterialPageRoute( - builder: (_) => const QrScreen(), - )); - }, - child: Container( - width: 315, - height: 65, - decoration: const BoxDecoration( - color: Colors.black + body: Center( + child: SingleChildScrollView( + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 32.0), + child: Column( + mainAxisSize: MainAxisSize.min, + // crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Flexible(flex: 2, child: Container()), + Image.asset( + "assets/log_2.png", + height: 250, + width: 300, + ), + const SizedBox( + height: 40, + ), + TextFieldInput( + icon: LineAwesomeIcons.envelope, + textEditingController: emailController, + hintText: "Enter your email", + textInputType: TextInputType.emailAddress), + const SizedBox( + height: 24, + ), + TextFieldInput( + icon: LineAwesomeIcons.lock, + textEditingController: passwordController, + hintText: "Enter your password", + textInputType: TextInputType.text, + isPass: true, + ), + const SizedBox( + height: 35, + ), + isLoading + ? const CircularProgressIndicator( + color: Colors.blue, + ) + : Container(), + const SizedBox( + height: 20, + ), + SubmitButton( + text: "Log In", + onTap: signInUser, ), - ), + ], ), ), - ], + ), ), ); } diff --git a/lib/screens/qr_screen.dart b/lib/screens/qr_screen.dart index e565cf0..bf43045 100644 --- a/lib/screens/qr_screen.dart +++ b/lib/screens/qr_screen.dart @@ -1,17 +1,31 @@ +import 'dart:ffi'; + import 'package:attendx/screens/id_screen.dart'; import 'package:attendx/screens/login_screen.dart'; +import 'package:attendx/services/postGres_services.dart'; import 'package:camera/camera.dart'; import 'package:flutter/material.dart'; +import 'package:mobile_scanner/mobile_scanner.dart'; import 'package:flutter/services.dart'; +import 'package:qr_scanner_overlay/qr_scanner_overlay.dart'; class QrScreen extends StatefulWidget { - const QrScreen({Key? key, }) : super(key: key); + const QrScreen({ + Key? key, + }) : super(key: key); @override State createState() => _QrScreenState(); } +String imageURL = ""; +const bgColor = Color(0xfffafafa); + class _QrScreenState extends State { + MobileScannerController cameraController = MobileScannerController(); + bool isScanCompleted = false; + bool isFrontCamera = false; + bool isFlashOn = false; CameraController? _controller; late bool _isFlashOn = false; @@ -21,6 +35,28 @@ class _QrScreenState extends State { initializeCamera(); } + void closeScreen() { + isScanCompleted = false; + } + + Future sendCodeToPostgres(String code) async { + String fileId = ""; + final string = await sendCodeToPost(code); + print(string); + final splitStrings = string.split('='); + if (splitStrings.length > 1) { + setState(() { + fileId = splitStrings[1]; + imageURL = "https://drive.google.com/uc?export=view&id=$fileId"; + print(imageURL); + }); + } else { + setState(() { + fileId = ""; + }); + } + } + Future initializeCamera() async { final cameras = await availableCameras(); if (cameras.isEmpty) { @@ -37,92 +73,205 @@ class _QrScreenState extends State { super.dispose(); } - Future toggleFlashlight() async { - try { - if (_controller == null) { - // Controller is not yet initialized - return; - } - if (_isFlashOn) { - await _controller!.setFlashMode(FlashMode.off); - } else { - await _controller!.setFlashMode(FlashMode.torch); + Future toggleFlashlight() async { + try { + if (_controller == null) { + // Controller is not yet initialized + return; + } + if (_isFlashOn) { + await _controller!.setFlashMode(FlashMode.off); + } else { + await _controller!.setFlashMode(FlashMode.torch); + } + setState(() { + _isFlashOn = !_isFlashOn; + }); + } catch (e) { + print('Error toggling flashlight: $e'); } - setState(() { - _isFlashOn = !_isFlashOn; - }); - } catch (e) { - print('Error toggling flashlight: $e'); } -} - + @override Widget build(BuildContext context) { return Scaffold( - body: Stack( - children: [ - Container( - height: MediaQuery.of(context).size.height, - width: MediaQuery.of(context).size.width, - decoration: BoxDecoration( - image: DecorationImage( - image: _isFlashOn ? const AssetImage("assets/qr_screen2.png") : const AssetImage("assets/qrscreen.png") , - fit: BoxFit.cover, - ), + backgroundColor: bgColor, + appBar: AppBar( + centerTitle: true, + title: const Text( + "AttendX", + style: TextStyle( + color: Colors.black87, + fontSize: 24, + fontWeight: FontWeight.bold, + letterSpacing: 1, ), ), - Positioned( - bottom: 20, - right: 20, - child: InkWell( - onTap: () { - Navigator.of(context).pushReplacement(MaterialPageRoute( - builder: (_) => const IdScreen(), - )); - }, - child: Container( - width: 100, - height: 50, - decoration: const BoxDecoration( - color: Colors.transparent - ), + actions: [ + IconButton( + color: Colors.white, + icon: Icon( + Icons.flash_on, + color: isFlashOn ? Colors.yellow : Colors.black, ), + iconSize: 32.0, + onPressed: () { + setState(() { + isFlashOn = !isFlashOn; + }); + cameraController.toggleTorch(); + }, ), - ), - Positioned( - bottom: 20, - left: 20, - child: InkWell( - onTap: () { - Navigator.of(context).pushReplacement(MaterialPageRoute( - builder: (_) => const LoginPage(), - )); + IconButton( + color: Colors.white, + icon: Icon( + Icons.flip_camera_android, + color: isFrontCamera ? Colors.blue : Colors.black, + ), + iconSize: 32.0, + onPressed: () { + setState(() { + isFrontCamera = !isFrontCamera; + }); + cameraController.switchCamera(); }, - child: Container( - width: 100, - height: 50, - decoration: const BoxDecoration( - color: Colors.transparent + ), + ], + ), + body: Container( + width: double.infinity, + padding: const EdgeInsets.all(16), + child: Column( + children: [ + const Expanded( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + "Place the QR code in the area", + style: TextStyle( + color: Colors.black87, + fontSize: 18, + fontWeight: FontWeight.bold, + letterSpacing: 1, + ), + ), + SizedBox( + height: 10, + ), + Text( + "Scanning will be started automatically", + style: TextStyle( + color: Colors.black54, + fontSize: 16, + ), + ) + ], ), ), - ), - ), - Positioned( - bottom: 175, - left: 145, - child: InkWell( - onTap: toggleFlashlight, - child: Container( - width: 80, - height: 40, - decoration: const BoxDecoration( - color: Colors.transparent + Expanded( + flex: 4, + child: Stack( + children: [ + MobileScanner( + controller: cameraController, + allowDuplicates: true, + onDetect: (barcode, args) { + if (!isScanCompleted) { + String code = barcode.rawValue ?? '---'; + isScanCompleted = true; + // sendCodeToPost(code); + Navigator.pushReplacement( + context, + MaterialPageRoute( + builder: (context) => IdScreen( + code: code, + closeScreen: closeScreen, + ), + ), + ); + } + }, + ), + QRScannerOverlay( + overlayColor: bgColor, + scanAreaHeight: 250, + scanAreaWidth: 250, + ), + ], ), ), - ), + const Expanded( + child: Text("Welcome to OSDHack by OSDC"), + ), + ], ), - ], - ), - ); + ) + + // Stack(S + // children: [ + // Container( + // height: MediaQuery.of(context).size.height, + // width: MediaQuery.of(context).size.width, + // decoration: BoxDecoration( + // image: DecorationImage( + // image: _isFlashOn ? const AssetImage("assets/qr_screen2.png") : const AssetImage("assets/qrscreen.png") , + // fit: BoxFit.cover, + // ), + // ), + // ), + // Positioned( + // bottom: 20, + // right: 20, + // child: InkWell( + // onTap: () { + // Navigator.of(context).pushReplacement(MaterialPageRoute( + // builder: (_) => const IdScreen(), + // )); + // }, + // child: Container( + // width: 100, + // height: 50, + // decoration: const BoxDecoration( + // color: Colors.transparent + // ), + // ), + // ), + // ), + // Positioned( + // bottom: 20, + // left: 20, + // child: InkWell( + // onTap: () { + // Navigator.of(context).pushReplacement(MaterialPageRoute( + // builder: (_) => const LoginPage(), + // )); + // }, + // child: Container( + // width: 100, + // height: 50, + // decoration: const BoxDecoration( + // color: Colors.transparent + // ), + // ), + // ), + // ), + // Positioned( + // bottom: 175, + // left: 145, + // child: InkWell( + // onTap: toggleFlashlight, + // child: Container( + // width: 80, + // height: 40, + // decoration: const BoxDecoration( + // color: Colors.transparent + // ), + // ), + // ), + // ), + // ], + // ), + ); } -} \ No newline at end of file +} diff --git a/lib/screens/success_screen.dart b/lib/screens/success_screen.dart index 8cdb991..73774e5 100644 --- a/lib/screens/success_screen.dart +++ b/lib/screens/success_screen.dart @@ -1,50 +1,163 @@ import 'package:attendx/screens/qr_screen.dart'; +import 'package:attendx/services/postGres_services.dart'; import 'package:flutter/material.dart'; class SuccessScreen extends StatefulWidget { - const SuccessScreen({Key? key, }) : super(key: key); + final int day; + const SuccessScreen({ + Key? key, + required this.day, + }) : super(key: key); @override State createState() => _SuccessScreenState(); } class _SuccessScreenState extends State { - + String value = particpiantIDfromPost.toString(); + String teamname = teamName.toString(); + bool isLoading = true; + + @override + void initState() { + // TODO: implement initState + print("Success screen"); + print(particpiantIDfromPost); + print(teamName); + print("printed participant id"); + super.initState(); + } + @override Widget build(BuildContext context) { return Scaffold( - body: Stack( + appBar: AppBar( + centerTitle: true, + title: const Text( + "AttendX", + style: TextStyle( + color: Colors.black87, + fontSize: 24, + fontWeight: FontWeight.bold, + letterSpacing: 1, + ), + ), + ), + body: Column( children: [ - Container( - height: MediaQuery.of(context).size.height, - width: MediaQuery.of(context).size.width, - decoration: const BoxDecoration( - image: DecorationImage( - image: AssetImage("assets/success.png"), - fit: BoxFit.cover, + const SizedBox( + height: 20, + ), + const Center( + child: Text( + "Attendance marked successfully!", + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 46, + fontWeight: FontWeight.bold, + color: Colors.black54, ), ), ), - Positioned( - bottom: 150, - right: 100, - child: InkWell( - onTap: () { - Navigator.of(context).pushReplacement(MaterialPageRoute( - builder: (_) => const QrScreen(), - )); - }, - child: Container( - width: 160, - height: 55, - decoration: const BoxDecoration( - color: Colors.transparent + const SizedBox( + height: 20, + ), + Column( + children: [ + const SizedBox( + height: 30, + ), + const Text( + "Participant ID", + style: TextStyle( + fontSize: 26, + fontWeight: FontWeight.w500, ), ), - ), - ), + const SizedBox( + height: 10, + ), + Text( + value, + style: const TextStyle( + fontSize: 22, + ), + ), + const SizedBox( + height: 20, + ), + const Text( + "Team Name", + style: TextStyle( + fontSize: 26, + fontWeight: FontWeight.w500, + ), + ), + const SizedBox( + height: 10, + ), + Text( + teamname, + style: const TextStyle( + fontSize: 22, + ), + ), + const SizedBox( + height: 20, + ), + const Text( + "For Day", + style: TextStyle( + fontSize: 26, + fontWeight: FontWeight.w500, + ), + ), + const SizedBox( + height: 10, + ), + Text( + widget.day == 1 + ? "Day 1" + : widget.day == 2 + ? "Day 2" + : "Day 3", + style: const TextStyle( + fontSize: 22, + ), + ), + const SizedBox( + height: 40, + ), + InkWell( + onTap: () { + Navigator.of(context).pushReplacement(MaterialPageRoute( + builder: (_) => const QrScreen(), + )); + }, + child: Container( + width: 140, + height: 60, + decoration: const BoxDecoration( + color: Color.fromARGB(255, 34, 122, 88), + borderRadius: BorderRadius.all(Radius.circular(12)), + ), + child: const Center( + child: Text( + "Done", + textAlign: TextAlign.center, + style: TextStyle( + color: Colors.white, + fontSize: 28, + fontWeight: FontWeight.bold, + ), + ), + ), + ), + ), + ], + ) ], ), ); } -} \ No newline at end of file +} diff --git a/lib/services/postGres_services.dart b/lib/services/postGres_services.dart new file mode 100644 index 0000000..7d971ad --- /dev/null +++ b/lib/services/postGres_services.dart @@ -0,0 +1,149 @@ +import 'package:postgres/postgres.dart'; + +late final connection; +Object? particpiantIDfromPost; +Object? teamName; + +Future connectWithPost() async { + print('in connection function'); + connection = await Connection.open( + Endpoint( + host: , + database: , + username: , + password: , + port: , + ), + settings: const ConnectionSettings(sslMode: SslMode.require), + ); + print('has connection!'); +} + +Future sendCodeToPost(String code) async { + print('in send code'); + print(code); + + final result = await connection.execute( + Sql.named('SELECT id_card FROM public.hashes WHERE "hash" = @code'), + parameters: {'code': code}, + ); + print(result[0][0]); + print('code sent'); + return result[0][0]; +} + +Future markAttendance1(String code) async { + final connection = await Connection.open( + Endpoint( + host: , + database: , + username: , + password: , + port: , + ), + settings: const ConnectionSettings(sslMode: SslMode.require), + ); + + final idResult = await connection.execute( + Sql.named('SELECT participantid FROM public.hashes WHERE "hash" = @code'), + parameters: {'code': code}, + ); + + final participantId = idResult[0][0]; + particpiantIDfromPost = participantId; + print("object"); + print(particpiantIDfromPost); + + final team = await connection.execute( + Sql.named('SELECT "teamname" FROM public.team_identifier WHERE "Enrollment No. (Team-Leader)" = ( SELECT "Enrollment No. (Team-Leader)" FROM public.participant_attendance WHERE "Participant_Id" = @x);'), + parameters: {'x': participantId}, + ); + + final teamname = team[0][0]; + teamName = teamname; + print("team"); + print(teamName); + + + await connection.execute( + Sql.named('UPDATE public.participant_attendance SET attendance_1 = True WHERE "Participant_Id" = @x'), + parameters: {'x': participantId}, + ); +} + +Future markAttendance2(String code) async { + final connection = await Connection.open( + Endpoint( + host: , + database: , + username: , + password: , + port: , + ), + settings: const ConnectionSettings(sslMode: SslMode.require), + ); + final idResult = await connection.execute( + Sql.named('SELECT participantid FROM public.hashes WHERE "hash" = @code'), + parameters: {'code': code}, + ); + + final participantId = idResult[0][0]; + particpiantIDfromPost = participantId; + print("object"); + print(particpiantIDfromPost); + + final team = await connection.execute( + Sql.named('SELECT "teamname" FROM public.team_identifier WHERE "Enrollment No. (Team-Leader)" = ( SELECT "Enrollment No. (Team-Leader)" FROM public.participant_attendance WHERE "Participant_Id" = @x);'), + parameters: {'x': participantId}, + ); + + final teamname = team[0][0]; + teamName = teamname; + print("team"); + print(teamName); + + await connection.execute( + Sql.named('UPDATE public.participant_attendance SET attendance_2 = True WHERE "Participant_Id" = @x'), + parameters: {'x': participantId}, + ); + +} + +Future markAttendance3(String code) async { + final connection = await Connection.open( + Endpoint( + host: , + database: , + username: , + password: , + port: , + ), + settings: const ConnectionSettings(sslMode: SslMode.require), + ); + final idResult = await connection.execute( + Sql.named('SELECT participantid FROM public.hashes WHERE "hash" = @code'), + parameters: {'code': code}, + ); + + final participantId = idResult[0][0]; + particpiantIDfromPost = participantId; + print("object"); + print(particpiantIDfromPost); + + final team = await connection.execute( + Sql.named('SELECT "teamname" FROM public.team_identifier WHERE "Enrollment No. (Team-Leader)" = ( SELECT "Enrollment No. (Team-Leader)" FROM public.participant_attendance WHERE "Participant_Id" = @x);'), + parameters: {'x': participantId}, + ); + + final teamname = team[0][0]; + teamName = teamname; + print("team"); + print(teamName); + + await connection.execute( + Sql.named('UPDATE public.participant_attendance SET attendance_3 = True WHERE "Participant_Id" = @x'), + parameters: {'x': participantId}, + ); + + return participantId; +} diff --git a/lib/widgets/submit_button.dart b/lib/widgets/submit_button.dart new file mode 100644 index 0000000..ad0be27 --- /dev/null +++ b/lib/widgets/submit_button.dart @@ -0,0 +1,36 @@ +import 'package:flutter/material.dart'; + +class SubmitButton extends StatelessWidget { + final String text; + final VoidCallback onTap; + const SubmitButton({Key? key, required this.text, required this.onTap}) + : super(key: key); + + @override + Widget build(BuildContext context) { + return InkWell( + onTap: onTap, + child: Container( + width: double.infinity, + alignment: Alignment.center, + padding: const EdgeInsets.symmetric(vertical: 12), + decoration: const ShapeDecoration( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.all( + Radius.circular(20), + ), + ), + color: Colors.blue, + ), + child: Text( + text, + style: const TextStyle( + color: Colors.white, + fontWeight: FontWeight.w600, + fontSize: 16, + ), + ), + ), + ); + } +} diff --git a/lib/widgets/text_field_input.dart b/lib/widgets/text_field_input.dart new file mode 100644 index 0000000..8ccf019 --- /dev/null +++ b/lib/widgets/text_field_input.dart @@ -0,0 +1,49 @@ +import 'package:flutter/material.dart'; + +class TextFieldInput extends StatelessWidget { + const TextFieldInput( + {Key? key, + required this.textEditingController, + this.isPass = false, + required this.hintText, + required this.textInputType, required this.icon}) + : super(key: key); + final TextEditingController textEditingController; + final bool isPass; + final String hintText; + final TextInputType textInputType; + final IconData icon; + + @override + Widget build(BuildContext context) { + final inputBorder = OutlineInputBorder( + borderRadius: BorderRadius.circular(8), + borderSide: Divider.createBorderSide(context), + ); + return TextField( + controller: textEditingController, + decoration: InputDecoration( + icon: Icon( + icon, + color: Colors.blue, + ), + focusColor: Colors.blue, + hintText: hintText, + border: inputBorder, + focusedBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(8), + borderSide: const BorderSide(color: Colors.blue), + ), + enabledBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(8), + borderSide: const BorderSide(color: Colors.blue), + ), + filled: true, + contentPadding: + const EdgeInsets.symmetric(horizontal: 16, vertical: 14), + ), + keyboardType: textInputType, + obscureText: isPass, + ); + } +} diff --git a/scripts/mail-scripts/emails.json b/scripts/mail-scripts/emails.json index 376a285..f9e0e23 100644 --- a/scripts/mail-scripts/emails.json +++ b/scripts/mail-scripts/emails.json @@ -1,7 +1,9 @@ { "subscribers": [ "shivang.sharma2062@gmail.com", - "krishkushjain@gmail.com", - "rahulkalra045@gmail.com" + "madhavmehndiratta2k3@gmail.com", + "rahulkalra045@gmail.com", + "goyaltanish789@gmail.com", + "krishkushjain@gmail.com" ] -} \ No newline at end of file +} diff --git a/scripts/mail-scripts/go.mod b/scripts/mail-scripts/go.mod index bbc90f8..f242172 100644 --- a/scripts/mail-scripts/go.mod +++ b/scripts/mail-scripts/go.mod @@ -3,8 +3,10 @@ module github.com/osdc/Mercurius go 1.15 require ( - github.com/yuin/goldmark v1.3.5 + github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e // indirect + github.com/yuin/goldmark v1.4.13 github.com/yuin/goldmark-meta v1.0.0 // indirect + golang.org/x/crypto v0.22.0 // indirect gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df // indirect ) diff --git a/scripts/mail-scripts/go.sum b/scripts/mail-scripts/go.sum index 8b8a9fe..1e945c0 100644 --- a/scripts/mail-scripts/go.sum +++ b/scripts/mail-scripts/go.sum @@ -1,8 +1,54 @@ +github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e h1:MRM5ITcdelLK2j1vwZ3Je0FKVCfqOLp5zO6trqMLYs0= +github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e/go.mod h1:XV66xRDqSt+GTGFMVlhk3ULuV0y9ZmzeVGR4mloJI3M= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.3.5 h1:dPmz1Snjq0kmkz159iL7S6WzdahUTHnHB5M56WFVifs= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +github.com/yuin/goldmark v1.4.13 h1:fVcFKWvrslecOb/tg+Cc05dkeYx540o0FuFt3nUVDoE= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/yuin/goldmark-meta v1.0.0 h1:ScsatUIT2gFS6azqzLGUjgOnELsBOxMXerM3ogdJhAM= github.com/yuin/goldmark-meta v1.0.0/go.mod h1:zsNNOrZ4nLuyHAJeLQEZcQat8dm70SmB2kHbls092Gc= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= +golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30= +golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= +golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= +golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= +golang.org/x/term v0.19.0/go.mod h1:2CuTdWZ7KHSQwUzKva0cbMg6q2DMI3Mmxp+gKJbskEk= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc h1:2gGKlE2+asNV9m7xrywl36YYNnBG5ZQ0r/BOOxqPpmk= gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc/go.mod h1:m7x9LTH6d71AHyAX77c9yqWCCa3UKHcVEj9y7hAtKDk= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/scripts/mail-scripts/main.go b/scripts/mail-scripts/main.go index 1449e5f..56a081e 100644 --- a/scripts/mail-scripts/main.go +++ b/scripts/mail-scripts/main.go @@ -2,6 +2,8 @@ package main import ( "bytes" + "crypto/sha256" + "encoding/hex" "encoding/json" "fmt" "html" @@ -10,6 +12,7 @@ import ( "log" "os" + "github.com/skip2/go-qrcode" "github.com/yuin/goldmark" meta "github.com/yuin/goldmark-meta" "github.com/yuin/goldmark/parser" @@ -28,17 +31,14 @@ func main() { ) jsonFile, err := os.Open("emails.json") - if err != nil { log.Print(err) } - defer jsonFile.Close() byteValue, _ := ioutil.ReadAll(jsonFile) var list subscriberList - json.Unmarshal(byteValue, &list) content, _ := ioutil.ReadFile("../../content/post/example.md") @@ -56,7 +56,6 @@ func main() { t, _ = t.ParseFiles("template.html") var body bytes.Buffer - if err := t.Execute(&body, struct { Content string Title string @@ -68,10 +67,40 @@ func main() { } html := html.UnescapeString(body.String()) - send(html, list.Subscribers) + emailToHash := make(map[string]string) + for _, email := range list.Subscribers { + // Use SHA-256 to hash the email + hash := sha256.Sum256([]byte(email)) + // Convert the hash to a hex string + hashHex := hex.EncodeToString(hash[:]) + // Store the email-to-hash mapping + emailToHash[email] = hashHex + } + + // Print the emailToHash map + printEmailToHash(emailToHash) + + // Generate QR codes for each hash + for email, hashHex := range emailToHash { + fileName := fmt.Sprintf("./qrcodes/%s.png", hashHex) + err := qrcode.WriteFile(hashHex, qrcode.Medium, 256, fileName) + if err != nil { + log.Printf("Failed to generate QR code for %s: %v", email, err) + } + } + + // Send emails with QR code attachments + send(html, list.Subscribers, emailToHash) } -func send(body string, to []string) { +func printEmailToHash(emailToHash map[string]string) { + fmt.Println("Email-to-Hash Map:") + for email, hash := range emailToHash { + fmt.Printf("Email: %s, Hash: %s\n", email, hash) + } +} + +func send(body string, to []string, emailToHash map[string]string) { from := os.Getenv("MAIL_ID") pass := os.Getenv("MAIL_PASSWORD") @@ -86,14 +115,25 @@ func send(body string, to []string) { log.Fatal(err) } - m := gomail.NewMessage() for _, r := range to { fmt.Printf("Sending email to: %s\n", r) + m := gomail.NewMessage() m.SetHeader("From", from) m.SetAddressHeader("To", r, r) m.SetHeader("Subject", "OSDHACK '24 Attendance QR") m.SetBody("text/html", string(bodyContent)) + // Get the hash for the current email + hashHex, ok := emailToHash[r] + if ok { + // Path to the QR code file + qrFilePath := fmt.Sprintf("./qrcodes/%s.png", hashHex) + // Attach the QR code file to the email and rename it to "qr.png" + m.Attach(qrFilePath, gomail.Rename("qr.png")) + } else { + log.Printf("Could not find hash for email %q", r) + } + if err := gomail.Send(s, m); err != nil { log.Printf("Could not send email to %q: %v", r, err) }