168 lines
3.8 KiB
Go

package persistence
import (
_ "embed"
"fmt"
"os"
"path"
"database/sql"
_ "github.com/mattn/go-sqlite3"
"gopkg.in/yaml.v2"
"offline_twitter/scraper"
)
//go:embed schema.sql
var sql_init string
type Settings struct {}
type Profile struct {
ProfileDir string
UsersList []scraper.UserHandle
Settings Settings
DB *sql.DB
}
/**
* Custom error
*/
type ErrTargetAlreadyExists struct {
target string
}
func (err ErrTargetAlreadyExists) Error() string {
return fmt.Sprintf("Target already exists: %s", err.target)
}
/**
* Create a new profile in the given location.
* Fails if target location already exists (i.e., is a file or directory).
*
* args:
* - target_dir: location to create the new profile directory
*
* returns:
* - the newly created Profile
*/
func NewProfile(target_dir string) (Profile, error) {
if file_exists(target_dir) {
return Profile{}, ErrTargetAlreadyExists{target_dir}
}
user_list_file := path.Join(target_dir, "users.txt")
settings_file := path.Join(target_dir, "settings.yaml")
sqlite_file := path.Join(target_dir, "twitter.db")
profile_images_dir := path.Join(target_dir, "profile_images")
images_dir := path.Join(target_dir, "images")
videos_dir := path.Join(target_dir, "videos")
// Create the directory
fmt.Printf("Creating new profile: %s\n", target_dir)
err := os.Mkdir(target_dir, os.FileMode(0755))
if err != nil {
return Profile{}, err
}
// Create `twitter.db`
fmt.Printf("Creating............. %s\n", sqlite_file)
db, err := sql.Open("sqlite3", sqlite_file + "?_foreign_keys=on")
if err != nil {
return Profile{}, err
}
_, err = db.Exec(sql_init)
if err != nil {
return Profile{}, err
}
// Create `users.txt`
fmt.Printf("Creating............. %s\n", user_list_file)
err = os.WriteFile(user_list_file, []byte{}, os.FileMode(0644))
if err != nil {
return Profile{}, err
}
// Create `settings.yaml`
fmt.Printf("Creating............. %s\n", settings_file)
settings := Settings{}
data, err := yaml.Marshal(&settings)
if err != nil {
return Profile{}, err
}
err = os.WriteFile(settings_file, data, os.FileMode(0644))
if err != nil {
return Profile{}, err
}
// Create `profile_images`
fmt.Printf("Creating............. %s/\n", profile_images_dir)
err = os.Mkdir(profile_images_dir, os.FileMode(0755))
if err != nil {
return Profile{}, err
}
// Create `images`
fmt.Printf("Creating............. %s/\n", images_dir)
err = os.Mkdir(images_dir, os.FileMode(0755))
if err != nil {
return Profile{}, err
}
// Create `videos`
fmt.Printf("Creating............. %s/\n", videos_dir)
err = os.Mkdir(videos_dir, os.FileMode(0755))
if err != nil {
return Profile{}, err
}
return Profile{target_dir, []scraper.UserHandle{}, settings, db}, nil
}
/**
* Loads the profile at the given location. Fails if the given directory is not a Profile.
*
* args:
* - profile_dir: location to check for the profile
*
* returns:
* - the loaded Profile
*/
func LoadProfile(profile_dir string) (Profile, error) {
user_list_file := path.Join(profile_dir, "users.txt")
settings_file := path.Join(profile_dir, "settings.yaml")
sqlite_file := path.Join(profile_dir, "twitter.db")
for _, file := range []string{
user_list_file,
settings_file,
sqlite_file,
} {
if !file_exists(file) {
return Profile{}, fmt.Errorf("Invalid profile, could not find file: %s", file)
}
}
users_data, err := os.ReadFile(user_list_file)
if err != nil {
return Profile{}, err
}
users_list := parse_users_file(users_data)
settings_data, err := os.ReadFile(settings_file)
if err != nil {
return Profile{}, err
}
settings := Settings{}
err = yaml.Unmarshal(settings_data, &settings)
if err != nil {
return Profile{}, err
}
db, err := sql.Open("sqlite3", sqlite_file + "?_foreign_keys=on")
if err != nil {
return Profile{}, err
}
return Profile{profile_dir, users_list, settings, db}, nil
}